Handle credit card info on Rails - javascript

How can I validate credit card informations on the client side without passing any info to my server (PCI compliance) ?
The API that I using respond my request with a token, the only info that I need to store on my database. I send the credit card infos for them, and they respond with a token.
I already have the methods to validate the credit card via javascript and make an API call, javascript too, but the info are passing in my server, as I can see on server log:
Started POST "/api_checkout" for 127.0.0.1 at 2014-08-04 21:53:02 -0300
Processing by Marketplace::CheckoutController#api_buy as JS
Parameters: {"utf8"=>"✓", "number"=>"4111 1111 1111 1111", "verification_value"=>"123", "full_name"=>"Test user", "expiration"=>"14/15", "token"=>"B5E7A1F1-9822-4433-9FEE-30B625B8B070"}
Rendered marketplace/checkout/api_buy.js.erb (0.4ms)
Completed 200 OK in 43ms (Views: 39.2ms | ActiveRecord: 0.0ms | Solr: 0.0ms)
Started POST "/api_checkout" for 127.0.0.1 at 2014-08-04 21:53:03 -0300
Processing by Marketplace::CheckoutController#api_buy as HTML
Parameters: {"utf8"=>"✓", "number"=>"4111 1111 1111 1111", "verification_value"=>"123", "full_name"=>"Test user", "expiration"=>"14/15", "token"=>"B5E7A1F1-9822-4433-9FEE-30B625B8B070"}
Completed 500 Internal Server Error in 21ms (no problem here, didn`t made the view yet)
I dont know if I`m doing the right things, but here what I have today
Here is my form:
<%= form_tag api_buy_path,:method => :post, :id => 'payment-form', :remote => true do %>
<div class="usable-creditcard-form">
<div class="wrapper">
<div class="input-group nmb_a">
<div class="icon ccic-brand"></div>
<%= text_field_tag :number, params[:number], :class=>"credit_card_number", :"data-api"=>"number" %>
</div>
<div class="input-group nmb_b">
<div class="icon ccic-cvv"></div>
<%= text_field_tag :verification_value, params[:verification_value], :class=>"credit_card_cvv", :"data-api"=>"verification_value" %>
</div>
<div class="input-group nmb_c">
<div class="icon ccic-name"></div>
<%= text_field_tag :full_name, params[:full_name], :class=>"credit_card_name", :"data-api"=>"full_name" %>
</div>
<div class="input-group nmb_d">
<div class="icon ccic-exp"></div>
<%= text_field_tag :expiration, params[:expiration], :class=>"credit_card_expiration", :"data-api"=>"expiration" %>
</div>
</div>
</div>
<div class="token-area">
<%= label_tag :token, "Card token:"%>
<%= text_field_tag :token, params[:token],:id=>"token", :readonly=> true, :value=>""%>
</div>
<%= submit_tag 'Submit' %>
<% end %>
Here is the javascript that I`m using:
SomeAPI.setAccountID("some-id");
SomeAPI.setTestMode(true);
jQuery(function($) {
$('#payment-form').submit(function(evt) {
var form = $(this);
var cardNumber = document.getElementById("number").value;
//Check with the number is valid
if(SomeAPI.utils.validateCreditCardNumber(cardNumber)){
var brand = SomeAPI.utils.getBrandByCreditCardNumber(cardNumber);
var cvv = document.getElementById("verification_value").value;
//Check the CVV by brand
if(SomeAPI.utils.validateCVV(cvv, brand)){
var expiration = document.getElementById("expiration").value.split("/");
var expiration_year = expiration[1];
var expiration_month = expiration[0];
//Check the Expiration Date
if(SomeAPI.utils.validateExpiration(expiration_month, expiration_year)){
var name = document.getElementById("full_name").value.split(" ");
var firstName = name[0];
var lastName = name[name.length - 1];
//Check everything
cc = SomeAPI.CreditCard(cardNumber, expiration_month, expiration_year, firstName, lastName, cvv);
var tokenResponseHandler = function(data) {
if (data.errors) {
//console.log(data.errors);
alert("Error: " + JSON.stringify(data.errors));
}
else {
$("#token").val( data.id );
form.get(0).submit();
}
// Send the form
form.trigger("submit.rails");
}
SomeAPI.createPaymentToken(cc, tokenResponseHandler);
return true;
}else{
alert("Invalid")
return false;
}
}else{
alert("Invalid")
return false;
}
}else{
alert("Invalid")
return false
}
});
});

The issue is you need to clear out the sensitive form data before calling form.submit:
$("#token").val( data.id );
form.get(0).submit();
The form needs the sensitive credit card data to perform the validation, however your Rails app does not need this data - it only needs the token.
I typically set the card number to a string containing just the last four digits of the card number like: ** ** **** 1234
Your SomeAPI may already be returning a masked CC back to you, otherwise you'll need to do it via Javascript.

Related

Javascript/JQuery error message didn't hide in Safari

In my registration form I have some validation logic which works perfectly fine in Chrome but in Safari error message don't disappear below form after filling empty fields. It looks like show() and hide() doesn't work and I don't now why because based on this https://www.quirksmode.org/dom/events/index.html it should worked.
if (registrationsForm.length > 0) {
var emailField = $('#users-registrations-email');
var emailConfirmationField = $('#users-registrations-email-confirmation');
var emailInvalidMsg = $('.bank-employees-users-registration__registrations__not-identical-email');
var obligatoryInvalidMsg = $('.bank-employees-users-registration__registrations__email--invalid');
submit.on('click', function(e) {
e.preventDefault();
if (emailField.val() !== emailConfirmationField.val() && emailField.length > 0) {
emailInvalidMsg.show();
emailField.addClass("invalid");
emailConfirmationField.addClass("invalid");
emailConfirmationField[0].setCustomValidity('Incorrect confirmation email');
if (emailField.val() !== '') obligatoryInvalidMsg.hide();
} else {
emailConfirmationField[0].setCustomValidity('');
}
validateEmail();
var invalidInput = $('input:invalid');
if (invalidInput.length === 0 && !fileInput.hasClass('invalid')) {
form.submit();
} else {
invalidInput.addClass('invalid');
validateInput();
}
});
}
Function which is responsible for input validation:
function validateInput() {
$('input').change(function() {
if ($(this).is(':valid')) {
$(this).removeClass('invalid');
}
});
}
Edit
This is a code snippet from view
new.html.erb
<div class="floating-label bank-employees-users-registration__registrations-input--wrapper">
<%= f.email_field :email, class: "bank-employees-users-registration__registrations-input floating-field", id: "users-registrations-email", placeholder: t('.email'), required: true, aria_required: true %>
<%= f.label :email, t('.email'), class: "floating-label-placeholder" %>
<span class="bank-employees-users-registration__registrations__not-identical-email">
<%= t('.email_not_identical') %>
</span>
<span
class="bank-employees-users-registration__registrations-input--invalid-msg bank-employees-users-registration__registrations__email--invalid"
id="bank-employees-users-registration__registrations__email--invalid">
<%= t'.obligatory' %></span>
<% if #registration_form.errors.full_messages_for(:email).first %>
<div class="alert alert-danger">
<div class="error-explanation">
<%= t('activerecord.errors.models.user.attributes.email.taken') %>
</div>
</div>
<% end %>
</div>
Edit2
Maybe this will be helpful - as you see there are some email validations where two email address has to be equal. When I provide different email address it shows me error message that they are not equal but if I correct them, the error will be changed to - this field is required. I was trying to implement this solution jquery .show() and .hide() not working in safari - adding spinner to <a href but without any positive results.
Why not get rid of all that and use HTML5 fields? They have client-side validations that work across browsers and you can validate input on the server side using model validations, let Rails deal with all that.
I know it may be a bit of upfront investment into refactoring all that but otherwise things will only get worse in the long term if you keep doing what you're doing.
Also look at the Bootstrap form gem in case you're using Bootstrap.

Using Materialize `chip` and `autocomplete` in Ruby on Rails Form with Associated Models

I am trying to create a form so a user can save a setting which has their default teams (multiple) and their professions (single). I can do this using simple_form and the lines of code below, but I am trying to use autocomplete as the dropdown lists do not work well with my design.
<%= f.association :profession %>
<%= f.association :team, input_html: { multiple: true } %>
I am loading the JSON from a collection into an attribute data-autocomplete-source within my inputs, a short bit of jquery then cycles through each of these and then initialises the materialize .autocomplete, I also need to do this with .chips for many associations.
The UI element is working as I would like, but I cannot work out how to save a new record. I have two problems:
Unpermitted parameters: :team_name, :profession_name - I've been trying to adapt this tutorial and believed that Step 11 would effectively translate this within the model, but am clearly not understanding something...
"setting"=>{"team_name"=>"", "profession_name"=>"Consultant Doctor"} - the team_name values (i.e. the chips) are not being recognised when attempting to save the record. I've got some nasty jquery that transfers the id from the div to the generated input which I was hoping would work...
I've also checked many previous questions on Stack Overflow (some of which seem to be similar to this question, generally using jqueryui) but cannot work out how to adapt the answers.
How can I use the names from a model in a materialize chip and autocomplete input and save the selections by their associated id into a record?
Any help or guidance would be much appreciated.
setting.rb
class Setting < ApplicationRecord
has_and_belongs_to_many :team, optional: true
belongs_to :user
belongs_to :profession
def team_name
team.try(:name)
end
def team_name=(name)
self.team = Team.find_by(name: name) if name.present?
end
def profession_name
profession.try(:name)
end
def profession_name=(name)
self.profession = Profession.find_by(name: name) if name.present?
end
end
settings_controller.rb
def new
#user = current_user
#professions = Profession.all
#teams = Team.all
#setting = Setting.new
#teams_json = #teams.map(&:name)
#professions_json = #professions.map(&:name)
render layout: "modal"
end
def create
#user = current_user
#setting = #user.settings.create(setting_params)
if #setting.save
redirect_to action: "index"
else
flash[:success] = "Failed to save settings"
render "new"
end
end
private
def setting_params
params.require(:setting).permit(:user_id, :contact, :view, :taketime, :sortname, :sortlocation, :sortteam, :sortnameorder, :sortlocationorder, :sortteamorder, :location_id, :profession_id, :department_id, team_ids: [])
end
views/settings/new.html.erb
<%= simple_form_for #setting do |f| %>
<div class="row">
<div class="col s12">
<div class="row">
<div class="input-field autocomplete_dynamic col s12">
<i class="material-icons prefix">group</i>
<div data-autocomplete-source='<%= #teams_json %>' class="string optional chips" type="text" name="setting[team_name]" id="setting_team_name"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col s12">
<div class="row">
<div class="input-field autocomplete_dynamic col s12">
<i class="material-icons prefix">group</i>
<%= f.input :profession_name, wrapper: false, label: false, as: :search, input_html: {:data => {autocomplete_source: #professions_json} } %>
<label for="autocomplete-input">Select your role</label>
</div>
</div>
</div>
</div>
<%= f.submit %>
<% end %>
$("*[data-autocomplete-source]").each(function() {
var items = [];
var dataJSON = JSON.parse($(this).attr("data-autocomplete-source"));
var i;
for (i = 0; i < dataJSON.length; ++i) {
items[dataJSON[i]] = null;
}
if ($(this).hasClass("chips")) {
$(this).chips({
placeholder: $(this).attr("placeholder"),
autocompleteOptions: {
data: items,
limit: Infinity,
minLength: 1
}
});
// Ugly jquery to give the generated input the correct id and name
idStore = $(this).attr("id");
$(this).attr("id", idStore + "_wrapper");
nameStore = $(this).attr("name");
$(this).attr("name", nameStore + "_wrapper");
$(this).find("input").each(function() {
$(this).attr("id", idStore);
$(this).attr("name", nameStore);
});
} else {
$(this).autocomplete({
data: items,
});
}
});
.prefix~.chips {
margin-top: 0px;
}
<!-- jquery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Materialize CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<!-- Materialize JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<!-- Material Icon Webfont -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<div class="row">
<div class="col s12">
<div class="row">
<div class="input-field autocomplete_dynamic col s12">
<i class="material-icons prefix">group</i>
<div data-autocomplete-source='["Miss T","Mr C","Mr D","Medicine Take","Surgery Take"]' class="string optional chips" type="text" name="setting[team_name]" id="setting_team_name"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col s12">
<div class="row">
<div class="input-field autocomplete_dynamic col s12">
<i class="material-icons prefix">group</i>
<input class="string optional input-field" data-autocomplete-source='["Consultant Doctor","Ward Clerk","Nurse","Foundation Doctor (FY1)","Foundation Doctor (FY2)","Core Trainee Doctor (CT2)","Core Trainee Doctor (CT1)"]' type="text" name="setting[profession_name]"
id="setting_profession_name">
<label for="autocomplete-input">Select your role</label>
</div>
</div>
</div>
</div>
Gems and versions
ruby '2.5.0'
gem 'rails', '~> 5.2.1'
gem 'materialize-sass'
gem 'material_icons'
gem
'materialize-form'
gem 'simple_form', '>= 4.0.1'
gem
'client_side_validations'
gem 'client_side_validations-simple_form'
This is almost certainly not the best way of doing this, but it does work. Please offer suggestions and I will update this, or if someone adds a better answer I will happily mark it as correct. This solution doesn't require much in the way of controller/model changes and is largely done with a (comparatively) short bit of jquery/JS so can be easily repeated within a project.
I've managed to get both autocomplete and chips working with Ruby on Rails, utilising simple_form form helpers where possible.
Effectively I am storing the JSON into a custom attribute for each case and then parsing this with some jquery/javascript when the view is loaded before using this to initialise either autocomplete or chips.
Autocomplete values are translated from name to id within the controller.
Chip values are recognised client side with some JS, and inputs created with the correct name and id for simpleform to automatically save the values as an array to the hash.
Full explaination and code is below.
Thank you to Tom for his helpful comments and input.
autocomplete
Requires you to create an input under variable_name and then add additional functions in the model to translate the name into an id for saving. Effectively following this tutorial.
<%= f.input :profession_name, input_html: { data: { autocomplete: #professions_json } } %>
As you can see above, the only real difference from adding a typical simple_form association is the following:
f.input rather than f.association - ensures a textbox is rendered rather than a drop down
:model_name rather than :model - ensures that the controller recognises this is a name that needs to be converted into an object
input_html: { data: { autocomplete: #model_json } } - this adds a custom attribute with all your JSON data, this is parse by
You need to ensure the names of your model are unique.
chips
This is a little more involved, requiring extra javascript functions. The code attachs a callback to the event of adding or removing a chip, before cycling through each and adding a hidden input. Each input has a name attribute which matches what simple_form expects, so it is correctly added to the hash params before being submitted to the controller. I couldn't get it to translate multiple names in an array, so just got it to re-read the id from the original JSON and add that as the value of the input.
<div id="team_ids" placeholder="Add a team" name="setting[team_ids]" class="chips" data-autocomplete="<%= #teams_json %>"></div>
From above you can see there are the following deviations from simple_form convention:
<div> rather than a <% f.input %> as Materialize chips needs to be called on a div
placeholder="..." this text is used as a placeholder once the chips is initialised, this can be left blank / not included
name="setting[team_ids]" helps simple_form understand which model this applies to
class="chips" ensures that our javascript later knows to initialise chips on this element
data-autocomplete="<%= #teams_json %>" saves the JSON data as an attribute of the div for parsing later
Currently the code re-parses the original JSON attribute, it is possible to reference the JSON data that is created on initialisation of the chips, this is likely better but I could not get it to work.
Custom Input Element - someone more experience than myself might be able to play around with this and create a custom element for simple_form... it was beyond me unfortunately.
Ruby on Rails Code
settings_controller.rb
class SettingsController < ApplicationController
...
def new
#user = current_user
#setting = Setting.new
#professions = Profession.select(:name)
#teams = Team.select(:id, :name)
# Prepare JSON for autocomplete and chips
#teams_json = #teams.to_json(:only => [:id, :name] )
#professions_json = #professions.to_json(:only => [:name] )
end
....
private
def setting_params
params.require(:setting).permit( :profession_name, :user_id, :profession_id, team_ids: [])
end
setting.rb
class Setting < ApplicationRecord
has_and_belongs_to_many :teams, optional: true
belongs_to :user
belongs_to :profession, optional: true
def profession_name
profession.try(:name)
end
def profession_name=(name)
self.profession = Profession.find_by(name: name) if name.present?
end
_form.html.erb N.B. this is a partial, as denoted by the preceding underscore
<%= simple_form_for #setting, validate: true, remote: true do |f| %>
<%= f.input :profession_name, input_html: { data: { autocomplete: #professions_json } } %>
<div id="team_ids" placeholder="Add a team" name="setting[team_ids]" class="chips" data-autocomplete="<%= #teams_json %>"></div>
<%= f.submit %>
<% end %>
Demo
$(document).ready(function() {
// Cycle through anything with an data-autocomplete attribute
// Cannot use 'input' as chips must be innitialised on a div
$("[data-autocomplete]").each(function() {
var dataJSON = JSON.parse($(this).attr("data-autocomplete"));
// Prepare array for items and add each
var items = [];
var i;
for (i = 0; i < dataJSON.length; ++i) {
items[dataJSON[i].name] = null; // Could assign id to image url and grab this later? dataJSON[i].id
}
// Check if component needs to be a chips
if ($(this).hasClass("chips")) {
// Initialise chips
// Documentation: https://materializecss.com/chips.html
$(this).chips({
placeholder: $(this).attr("placeholder"),
autocompleteOptions: {
data: items,
limit: Infinity,
minLength: 1
},
onChipAdd: () => {
chipChange($(this).attr("id")); // See below
},
onChipDelete: () => {
chipChange($(this).attr("id")); // See below
}
});
// Tweak the input names, etc
// This means we can style the code within the view as we would a simple_form input
$(this).attr("id", $(this).attr("id") + "_wrapper");
$(this).attr("name", $(this).attr("name") + "_wrapper");
} else {
// Autocomplete is much simpler! Just initialise with data
// Documentation: https://materializecss.com/autocomplete.html
$(this).autocomplete({
data: items,
});
}
});
});
function chipChange(elementID) {
// Get chip element from ID
var elem = $("#" + elementID);
// In theory you can get the data of the chips instance, rather than re-parsing it
var dataJSON = JSON.parse(elem.attr("data-autocomplete"));
// Remove any previous inputs (we are about to re-add them all)
elem.children("input[auto-chip-entry=true]").remove();
// Find the wrapping element
wrapElement = elem.closest("div[data-autocomplete].chips")
// Get the input name we need, [] tells Rails that this is an array
formInputName = wrapElement.attr("name").replace("_wrapper", "") + "[]";
// Start counting entries so we can add value to input
var i = 0;
// Cycle through each chip
elem.children(".chip").each(function() {
// Get text of chip (effectively just excluding material icons 'close' text)
chipText = $(this).ignore("*").text();
// Get id from original JSON array
// You should be able to check the initialised Materialize data array.... Not sure how to make that work
var chipID = findElement(dataJSON, "name", chipText);
// ?Check for undefined here, will be rejected by Rails anyway...?
// Add input with value of the selected model ID
$(this).parent().append('<input value="' + chipID + '" multiple="multiple" type="hidden" name="' + formInputName + '" auto-chip-entry="true">');
});
}
// Get object from array of objects using property name and value
function findElement(arr, propName, propValue) {
for (var i = 0; i < arr.length; i++)
if (arr[i][propName] == propValue)
return arr[i].id; // Return id only
// will return undefined if not found; you could return a default instead
}
// Remove text from children, etc
$.fn.ignore = function(sel) {
return this.clone().find(sel || ">*").remove().end();
};
// Print to console instead of posting
$(document).on("click", "input[type=submit]", function(event) {
// Prevent submission of form
event.preventDefault();
// Gather input values
var info = [];
$(this).closest("form").find("input").each(function() {
info.push($(this).attr("name") + ":" + $(this).val());
});
// Prepare hash in easy to read format
var outText = "<h6>Output</h6><p>" + info.join("</br>") + "</p>";
// Add to output if exists, or create if it does not
if ($("#output").length > 0) {
$("#output").html(outText);
} else {
$("form").append("<div id='output'>" + outText + "</div>");
}
});
<!-- jquery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Materialize CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<!-- Materialize JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<!-- Material Icon Webfont -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<form class="simple_form new_setting" id="new_setting" novalidate="novalidate" data-client-side-validations="" action="/settings" accept-charset="UTF-8" data-remote="true" method="post"><input name="utf8" type="hidden" value="✓">
<div class="input-field col string optional setting_profession_name">
<input data-autocomplete='[{"id":1,"name":"Consultant Doctor"},{"id":2,"name":"Junior Doctor (FY1)"}]' class="string optional" type="text" name="setting[profession_name]" id="setting_profession_name"
data-target="autocomplete-options-30fe36f7-f61c-b2f3-e0ef-c513137b42f8" data-validate="true">
<label class="string optional" for="setting_profession_name">Profession name</label></div>
<div id="team_ids" name="setting[team_ids]" class="chips input-field" placeholder="Add a team" data-autocomplete='[{"id":1,"name":"Miss T"},{"id":2,"name":"Surgical Take"}]'></div>
<input type="submit" name="commit" value="Create Setting" data-disable-with="Create Setting">
</form>

Params is not passing for the second time form the controller to view in Rails

I am building "comments" model for "post". Single profile will be having two tabs. Each tabs will be having ten posts. So , I am calling post using UUID (Unique Id).On first time, comments get posted and index page for comments is shown. But for the next time, without refreshing the page, comments getting posted via ajax but index page is not shown.
My comments section in Posts/index is
<% #posts.each do |p| %>
<% a = SecureRandom.uuid %>
<i class="fa fa-comment post-comment coms" data-a="<%= a %>"
style="cursor:pointer;" data-postid="<%= p.id %>" data-class="<%=
p.class.to_s %>"></i>
<%= p.comments.count %>
<div id="comments_post_<%= a %>"></div>
<% end %>
<script type="text/javascript">
var comments_url = "<%= comments_path %>";
$(".coms").click(function(e){
var p_a = $(this).data("a");
var c_type = $(this).data("class");
var id = $(this).data("postid");
e.stopImmediatePropagation()
$("#comments_post_"+p_a).toggle(
function(){($("#comments_post_"+p_a).load(comments_url+"?c_type="+c_type+"&id="+id+"&p_uuid="+p_a));}
);
});
In jquery, I am passing the UUID as p_uuid to the comments index action.
comments/controller index action is
def index
#holder = params[:c_type].constantize.find(params[:id])
#c_uuid = params[:p_uuid] unless params[:p_uuid].blank?
render partial: "comments/index"
end
I am assigning params[:p_uuid] to the #c_uuid.
comments/index is
<div class="row postComment"">
<div class="col-md-2 col-sm-2"><%= image_tag(current_user.image_url[:url], :class => "commentpic") rescue "" %></div>
<div class="col-md-10 col-sm-10">
<textarea id="commentText" class="annotation send-comm form-control" placeholder="Add a comment"
data-c-id="<%= #holder.id %>" data-c-type="<%= #holder.class.to_s %>" data-uuid="<%= #c_uuid %>"></textarea>
<div class="hint">Press Shift + Enter for a new Line</div>
</div>
</div>
<script>
var comments_url = "<%= comments_path %>";
function postComment(context){
var comment = $(context).val();
var c_type = $(context).data("c-type");
var c_id = $(context).data("c-id");
var uuid=$(context).data("uuid");
var commParams = {
commentable_type: c_type,
commentable_id: c_id,
body: comment
}
postData( comments_url, { comment: commParams }, function(data){
$("#comments_"+"<%= #holder.class.to_s.downcase %>_<%= #c_uuid %>").load(comments_url+"?c_type=<%= #holder.class.to_s %>&id="+"<%= #holder.id %>");
});
</script>
Actual problem is, I am posting first comment, its getting posted and index page is shown. But , for the second time the comments getting saved in database but its not getting shown on index or index not getting updated.
I checked with the console.log, #c_uuid from comments controller in passed to the comments/index for the first time, but its not available for the second time. Means, #c_uuid is nil for the second time.Please, help me in this.

How can I update the message inside the data-confirmation attribute using Jquery?

I have a polymorphic association with Resources & Flags in my rails app. Each Resource can be Flagged by a user.
On the Resource Edit Page, an Admin can delete multiple flags associated with each Resource via Jquery. (This works fine)
I am then trying to display the total number of flags remaining in an alert message using the data-confirmation attribute before a user updates a Resource.
For Example:
<%= f.submit "Update Resource", data: { confirm: "Reminder: You have #{#flaggable.flags.count} flag alerts remaining." } %>
For some reason, my jquery updates the entire div button VS the variable inside the data-confirmation attribute.
How can I directly target the variable inside the data-confirmation attribute using Jquery?
Models
class Resource
belongs_to :district
has_many :flags, as: :flaggable, :dependent => :destroy
end
class Flag
belongs_to :flaggable, polymorphic: true
end
Resource Controller
#edit page
def edit
#district = District.find_by_abbreviation(params[:district_id])
#resource = #district.resources.find(params[:id])
#flaggable = #resource
#flags = #flaggable.flags.all
end
Views
edit.html.erb - Resource Edit Page
###Renders all flags & allows a user to delete flags via Javascript.
<div id="flags">
<%= render :partial => "flags/flag", :collection => #flags %>
</div>
###Resource Form
<div>
<%= simple_form_for ([#district, #district_resource]) do |f| %>
<div id="counter>
###How can I directly target the variable inside my alert message/data-confirm?
<%= f.submit "Update Resource", data: { confirm: "Reminder: You have #{#flaggable.flags.count} flag alerts remaining." } %>
</div>
<% end %>
</div>
_flag.html.erb (partial)
<div id="<%= dom_id(flag) %>" class="flag">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">
<%= link_to '×'.html_safe, [#flaggable, flag], method: :delete, remote: true %>
</span>
</button>
<b>Flag Alert: </b> <%= flag.message %>
</div>
destroy.js.erb
###This works and removes flags
$('#flags #<%= dom_id(#flag) %>').remove();
###This doesn't update the actual data-confirm attribute message
newFlagCount = $('#flags .flag').size();
$("#counter input[data-confirm]").data("Reminder: You have #{#flaggable.flags.count} flag alerts remaining.");
Well, $("#counter") is selecting the <div id="counter"> div and .html() is changing the entire div content. So, your code is doing exactly what you are asking it to do. You should target the submit button inside your "counter" div and change the data-confirm attribute's text.
$("#counter input[data-confirm]").data("Reminder: You have #{#flaggable.flags.count} flag alerts remaining.");
Please note, that if you are changing the number of flags using javascript, the variable in the text will be the original value (#flaggable.flags.count). You would need to regex the digit in your attribute text and change it using javascript.
put your JSON into a variable, then assign it into the element attribute:
var data = { confirm: "Reminder: You have #{#flaggable.flags.count} flag alerts remaining." }
$("#counter input").attr("data-confirm",data.confirm);
Combined the answer below to solve the problem. Thanks everyone.
newFlagCount = $('#flags .flag').length;
var data = { confirm: "Reminder: You have " + newFlagCount + " flag alerts remaining." }
$("#counter input[data-confirm]").data('confirm', data.confirm)

Passing nested parameters for instance variables

strong textI'm trying to make dynamic select boxes via "data-remote attribute for select boxes". In console it seems that i'm getting right parameters(the id of selected make), but I can't figure it out how to pass it to controller to get models with matching make_id:s.
Heres the attached_vehicles form part from _form.html.erb
<div class="vehicle_field">
<%= f.fields_for :attached_vehicles do |av| %>
<p>Select make</p>
<%= av.select :make, (#makes.collect { |m| [m.make_name, m.id] }), { include_blank: "Select make" }, { data: { remote: true, url: "update_make_models", name: "make", update: "#diy_attached_vehicles_attributes_0_model"} } %><br>
<p>Select model</p>
<%= av.collection_select :model, #models, (render "make_models/make_model"), { prompt: "Select model" } %><br>
...
<% end %>
</div>
../views/diys/update_make_models.coffee
$.empty()
.append("<%= escape_javascript(render "make_models/make_model") %>")
../diys_controller.rb
...
def update_make_models
#models = MakeModel.where("make_id = ?", params[:make])
end
def new
#diy = Diy.new
#step = #diy.steps.new
#attached_vehicle = #diy.attached_vehicles.new
#step.add_images_to_steps.new
#makes = Make.all
#models = MakeModel.where("make_id = ?", params[:make_id])
end
...
../views/make_models/_make_model.html.erb
<% #models.collect do |models| %>
<option value="<%= models.id %>"><%= models.make_model_name %></option>
<% end %>
And here's what i'm getting in console after selecting make in makes select box
Started GET "/diys/update_make_models?diy%5Battached_vehicles_attributes%5D%5B0%5D%5Bmake%5D=12" for ::1 at 2016-02-18 20:22:35 +0200 Processing by DiysController#update_make_models as JS
Parameters: {"diy"=>{"attached_vehicles_attributes"=>{"0"=>{"make"=>"12"}}}}
MakeModel Load (1.0ms) SELECT "make_models".* FROM "make_models" WHERE (make_id = NULL)
Rendered make_models/_make_model.html.erb (3.0ms)
Rendered diys/update_make_models.coffee (491.0ms)
Completed 200 OK in 628ms (Views: 626.5ms | ActiveRecord: 1.0ms | Solr: 0.0ms)
------------------------------------------------------------------------------------------------------------------------------------
Edit
NameError (undefined local variable or method `attached_vehicles_attributes' for #<DiysController:0x5757648>):
app/controllers/diys_controller.rb:28:in `update_make_models'
Your params hash is(according to logs): {"diy"=>{"attached_vehicles_attributes"=>{"0"=>{"make"=>"12"}}}}. So, if you want to get :make_id from it, you should write:
def update_make_models
#models = MakeModel.where(make_id: params["diy"]["attached_vehicles_attributes"]["0"]["make"])
end

Categories

Resources