I have a form_with that calls a controller that responds in JSON. I would like to run javascript (handle errors, ...) during an event ajax:success or ajax:error.
I followed the Rails documentation. Unfortunately, the variables in my events are undefined. Only e are set.
Form:
<%= form_with url: contact_path, scope: 'message', id: 'new_message' do |f| %>
<div class="row">
<div class="input-field col s6">
<%= f.text_field :name, class: 'validate' %>
<%= f.label :name, 'Nom' %>
</div>
<div class="input-field col s6">
<%= f.email_field :email, class: 'validate' %>
<%= f.label :email, 'Email' %>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<%= f.telephone_field :phone, class: 'validate' %>
<%= f.label :phone, 'Téléphone' %>
</div>
<div class="input-field col s6">
<%= f.text_field :website, class: 'validate' %>
<%= f.label :website, 'Site Web' %>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<%= f.text_area :content, class: 'materialize-textarea validate' %>
<%= f.label :content, 'Message' %>
</div>
</div>
<div class="row">
<div class="col s6">
<%= recaptcha_tags %>
</div>
<div class="col s6">
<%= button_tag class: 'btn primary waves-effect waves-light right' do %>
Envoyer
<i class="material-icons right">send</i>
<% end %>
</div>
</div>
<% end %>
Controller:
def create
#message = Message.new(message_params)
respond_to do |format|
if verify_recaptcha(model: #message) && #message.valid?
MessageMailer.new_message(#message).deliver
format.json { render json: #message, status: :created }
else
format.json { render json: #message.errors, status: :unprocessable_entity }
end
end
end
CoffeeScript:
$(document).ready ->
$("#new_message").on("ajax:success", (e, data, status, xhr) ->
console.log(xhr);
).on "ajax:error", (e, xhr, status, error) ->
console.log(xhr);
Result:
undefined message.self-5eda06948d26dade035273a7f168828c2cb1852e26937c49f50da7ca2532ad72.js:4:14
Started POST "/contact" for 127.0.0.1 at 2017-06-13 17:19:17 +0200
Processing by MessagesController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"aBxh+eJedXw0WifdUXFAlgH0gWxNpiDQa2J3hdJayC/wBaccIxNSVK4pycYwu9XMb1s2/r9S8+FteRFoocdrZQ==", "messages"=>{"name"=>"", "email"=>"", "phone"=>"", "website"=>"", "content"=>""}, "g-recaptcha-response"=>"03AOPBWq9SEfWUxzdyMRMdd0p65vonbGjNjA_mhCA07vfoXyA6_nEVn220Ot5HU90_KpQv6ziii0VCoa1hZosUcsARL7DuJbFHA2kt_NBHvnURmPpKR0SMq0OfulzJokvobdu9t1z33Uby3oTAtTgEAEmxX6yDc_yuwmqHXAjh5SL0bhAyD6AF2wAN8me7fMKMULu_DS5KtLgwJjK8B2TmvGydn0neHDWcfSbDKP7WRP-RQQePc9hDcOCbUFoVrW2kfpg3p4OV-hWiV4q6-HBRZK3A1mcj5rJZd_zBe3v-yx6LHAXB2Bsb9Uc", "button"=>""}
Completed 422 Unprocessable Entity in 2701ms (Views: 0.5ms | ActiveRecord: 856.3ms)
If you are using the rails-ujs adapter, according to this Rails Edge Guide:
Signature of calls to UJS's event handlers has changed. Unlike the
version with jQuery, all custom events return only one parameter:
event. In this parameter, there is an additional attribute detail
which contains an array of extra parameters.
I'd suggest taking a look at your app/assets/javascripts/application.js and find out which ujs adapter you are currently using. //= require rails-ujs is the new default.
Take a look at the example provided in the guide. You can extract data, status, and xhr in the following manner.
document.body.addEventListener('ajax:success', function(event) {
var detail = event.detail;
var data = detail[0], status = detail[1], xhr = detail[2];
})
Another alternative is to to use jquery_ujs which was the default prior to Rails 5.1 and has a signature more like you were expecting.
Related
I am venturing into AJAX and would like to delete a record of a model and followed this thread : How to perform Controller.destroy action asynchronously in rails?
models:
Photographe
Photographephoto
views/photographes/edit.html.erb
<div id="sectionimages" class="gutter-0 row" >
<% #photographe.photographephotos.all.each do |f| %>
<div class="col-md-2 col-sm-3 col-xs-6 col">
<%= link_to professionnel_photographe_photographephoto_path(#photographe.professionnel.id,#photographe.id, f.id), method: :delete, remote: 'true', id: "destruction"+f.id.to_s do %>
<div class="killimage" >
<%= image_tag f.image.url(:small) %>
<%= image_tag 'fatcrosswhite.svg', class: 'imgover' %>
</div>
<% end %>
</div>
<% end %>
</div>
link to Photographephotos#destroy is remote and there's an id like "destruction134"
photographephotos_controller.rb
def destroy
#imagedestroyed = Photographephoto.find(params[:id])
#imagedestroyedid = #imagedestroyed.id
#imagedestroyed.image = nil
#imagedestroyed.save
#imagedestroyed.destroy
respond_to do |format|
format.js
end
end
views/photographephotos/destroy.js.erb
var destroyelement = document.getElementById("<%= "destruction"+#imagedestroyedid.to_s %>");
destroyelement.parentNode.remove();
Problem is the file is indeed deleted but I have to refresh the page to see it removed. It seems the javascript is not effective. (I am trying to remove the parent of the link which is a Bootstrap DIV)
I am trying to implement AJAX in my to do type app, but am getting a missing template error (Missing template items/create, application/create with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee]}.) on my create action. There shouldn't be a create.html.erb page because the creation of items happens on a lists#show page:
<div class='new-item'>
<%= render 'items/form' %>
</div>
<div class="js-items">
<% #items.each do |item| %>
<%= div_for(item) do %>
<p><%= link_to "", list_item_path(#list, item), method: :delete, remote: true, class: 'glyphicon glyphicon-ok', style: "margin-right: 10px" %>
<%= item.name %>
<% if item.delegated_to != "" && item.user_id == current_user.id %>
<small>(Delegated to <%= item.delegated_to %>)</small>
<% elsif item.delegated_to != "" && item.user_id != current_user.id %>
<small>(Delegated by <%= item.user_id %>)</small>
<% end %></p>
<% end %>
<% end %>
</div>
And the _form.html.erb partial to which it links:
<div class="row">
<div class="col-xs-12">
<%= form_for [#list, #item], remote: true do |f| %>
<div class="form-group col-sm-6">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter Item Name" %>
</div>
<div class="form-group col-sm-6">
<%= f.label 'Delegate To (Not Required)' %>
<%= f.text_field :delegated_to, class: 'form-control', placeholder: "Enter an Email Address" %>
</div>
<div class="text-center"><%= f.submit "Add Item to List", class: 'btn btn-primary' %></div>
<% end %>
</div>
</div>
Here's the create method in the items_controller:
def create
#list = List.friendly.find(params[:list_id])
#item = #list.items.new(item_params)
#item.user = current_user
#new_item = Item.new
if #item.save
flash[:notice] = "Item saved successfully."
else
flash[:alert] = "Item failed to save."
end
respond_to do |format| <<<<ERROR CALLED ON THIS LINE
format.html
format.js
end
end
And here's my create.js.erb file:
<% if #item.valid? %>
$('.js-items').prepend("<%= escape_javascript(render(#item)) %>");
$('.new-item').html("<%= escape_javascript(render partial: 'items/form', locals: { list: #list, item: #new_item }) %>");
<% else %>
$('.flash').prepend("<div class='alert alert-danger'><button type='button' class='close' data-dismiss='alert'>×</button><%= flash.now[:alert] %></div>");
$('.new-item').html("<%= escape_javascript(render partial: 'items/form', locals: { list: #list, item: #item }) %>");
<% end %>
Anyone have any ideas why I am getting this? I tried removing the format.html line from the items_controller but I got an unrecognizable format error.
You can force the form submission to use a JS format, instead of the default HTML format, so that your respond_to block will render the create.js.erb view instead of the create.html.erb view.
<%= form_for([#list, #item], format: :js, remote: true) do |f| %>
Typically, Rails will detect your remote: true option via "unobtrusive JavaScript (ujs)", which will effectively handle the format: :js option for you. There is something unique in your setup that we have not identified, which is requiring you to enforce the format option.
To see that your create.js.erb is working, you can change the line that renders your item partial:
$('.js-items').prepend("<%= escape_javascript(render(#item)) %>");
To render a the item's name, wrapped in a <p> tag:
$('.js-items').prepend("<p>" + <%= #item.name %> + "</p>");
You can customize that line to generate the html that you want to appear for your new list item. Based on your code above, this looks like it will work out for you.
As you finish your to-do app, you can create a partial for your item, and then change that line again so that your partial will be appended to your list of items.
In my project I have two forms for two locales, so I have two EpicEditors on one page. However, it does not work well.
_form.html.erb
<div id="tabs-1">
<%= f.globalize_fields_for :ru do |g| %>
<div class="form-inputs">
<%= g.input_field :title, placeholder: t('.course_title') %>
<div id="epiceditor">
</div>
<%= g.input_field :description, id: "epiceditor-textarea" %>
</div>
<% end %>
</div>
<div id="tabs-2">
<%= f.globalize_fields_for :en do |g| %>
<div class="form-inputs">
<%= g.input_field :title, placeholder: t('.course_title') %>
<div id="epiceditor">
</div>
<%= g.input_field :description, id: "epiceditor-textarea" %>
</div>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit, t('.save'), class: "pull-right" %>
</div>
application.js
function LoadEditor(element) {
new EpicEditor({
container: element,
textarea: '#epiceditor-textarea',
theme: {
preview: '/assets/application.css'
}
}).load();
}
$("#epiceditor").each(function(index, element) {
LoadEditor(element)
});
What can you suggest I do?
Of course I can call method LoadEditor two times, but I think it is not a good solution, because then I will add more locales, maybe.
Using this code I have error:
Uncaught TypeError: Cannot read property 'value' of null
I have given a detailed answer at https://stackoverflow.com/a/35285968/936494. It shows passing custom options and how to use that to instantiate EpicEditor. Please have a look at it and try using the code given. I am sure it will be helpful.
Ruby on Rails 4.1
The form is placing the parameters into an array when POST is done. I am making a script to auto fill the billing address the same as the shipping address. When the script runs it is assigning the values as html values.
How do you put these values into the payment array being sent?
The log where cc_name and ship_name are outside the payment array, I want them inside:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"x", "cc_name"=>"Bobby", "payment"=>{"telephone"=>"555", "email"=>"drfernandez1#yahoo.com", "address1"=>"641 W Rt 66", "address2"=>"", "city"=>"Glendora", "state"=>"CA", "postal_code"=>"91740", "country"=>"USA", "cc_type"=>"Visa", "cc_number"=>"5555555588884444", "ccv"=>"321", "expires_on(1i)"=>"2014", "expires_on(2i)"=>"9", "expires_on(3i)"=>"1", "ship_address1"=>"641 W Route 66", "ship_address2"=>"", "ship_city"=>"Glendora", "ship_state"=>"c", "ship_postal_code"=>"91740", "ship_country"=>"u", "card_holder_auth"=>"1", "charge_auth"=>"1", "name_auth"=>"David Duke", "date_auth"=>"7"}, "billingtoo"=>"on", "ship_name"=>"Bobby", "commit"=>"Send Payment"}
The form (minus clutter):
<%= form_for #payment do |f| %>
<%= render 'shared/payment_error_messages' %>
<h1>Fill in all blank areas with payment information:</h1>
<div class="col-md-5 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Applicant Information</h3>
</div>
<div class="panel-body">
<%= f.label :cc_name, "Name as it appears on credit card:" %><br>
<%= f.text_field :cc_name, class: "input-lg", :name => "cc_name" %> <%#= #payment.errors.get(:cc_name) %>
<%= f.label :telephone, "Telephone Number:" %><br>
<%= f.text_field :telephone, class: "input-lg" %>
...
<div class="panel-body">
<input type="checkbox" name="billingtoo" onclick="FillBilling(this.form)"><em>Shipping Address is the same as the Billing Address</em>
<p>Please Note: This cannot be a P.O box address</p>
<%= f.label :ship_name, "Name:" %><br>
<%= f.text_field :ship_name, class: "input-lg", :name => "ship_name" %>
<%= f.label :ship_address1, "Address 1:" %><br>
<%= f.text_field :ship_address1, class: "input-lg" %>
...
<%= f.submit "Send Payment", class: "btn btn-lg btn-primary", id: "commit" %>
</div>
</div>
</div>
<% end %>
<br/>
<%= link_to 'Back', session[:last_page] %>
</div>
<script>
function FillBilling(f) {
if(f.billingtoo.checked == true) {
f.ship_name.value = f.cc_name.value;
}
}
</script>
You are overwriting the field's name:
:name => "cc_name"
That's the reason why it is sent outside of the array. Try removing the :name assigning:
<%= f.text_field :cc_name, class: "input-lg" %>
and
<%= f.text_field :ship_name, class: "input-lg" %>
I have this as Javascript code for my Ticket Model.
$(document).ready(function() {
$('#submitForm').click(function(e) {
$('#form1').submit();
$('#form2').submit();
});
});
On my ticket edit view, i have both ticket and its comments to update. Ticket form its named form1 and comment form is named form2. If I only have form1 on my javascript code, it updates perfectly, when I add form2 only form2 updates, so only the comment gets added, the Ticket Atributes (from form1) are not being updated.
Here is my Ticket View code (its actually a partial i'm loading on the edit view")
<%= form_for #ticket, :html => { :class => 'form-horizontal', :id => 'form1' } do |f| %>
<div class="control-group">
<%= f.label :subject, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :subject, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :content, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :content, :class => 'text_area' ,:style => 'height:190px; width:655px', disabled: true %>
</div>
</div>
<div class="control-group">
<%= f.label "Assigne", :class => 'control-label' %>
<div class="controls">
<%= f.collection_select(:user_id, User.all, :id, :email) %>
</div>
</div>
<div class="control-group">
<%= f.label :department_id, :class => 'control-label' %>
<div class="controls">
<%= f.collection_select(:department_id, Department.all, :id, :name) %>
</div>
</div>
<div class="control-group">
<%= f.label :status, :class => 'control-label' %>
<div class="controls">
<%= f.select :status, options_for_select(["Open", "In Progress", "Waiting-for-Info", "Closed"]), :class => 'from-control' %>
</div>
</div>
<div class="control-group">
<%= f.label :priority, :class => 'control-label' %>
<div class="controls">
<%= f.select :priority, options_for_select([ "Normal", "High", "Immediate", "Low"]), :class => 'from-control' %>
</div>
</div>
<div class="control-group">
<%= f.label :due_date, :class => 'control-label' %>
<div class="controls">
<%= f.date_select :due_date, :class => 'date_select' %>
</div>
</div>
<% end %>
<h4>Comments</h4>
<div id="comments">
<%= render :partial => #ticket.comments %>
</div>
<%= form_for [#ticket, Comment.new], :html => { :class => 'form-horizontal', :id => 'form2' } do |f| %>
<p>
<%= f.text_area :content, :class => 'text_area' ,:style => 'height:75px; width:600px' %></p></br>
<% end %>
<%= submit_tag nil, :class => 'btn btn-primary', :id => 'submitForm' %>
Am I doing something wrong?
When you call $('#form1').submit();, the page performs an http request. What this means is the page stops executing its javascript and begins performing the actions associated with said http request. (Usually this would be an http post to the form's target, where the data from the form is sent to the server.)
To bypass this, you need to look into ajax calls. An ajax call is an "Asynchronous JavaScript and XML" call, whereby you can perform http requests through your javascript code without interrupting anything else.
Ajax is a bit much to cover in an answer, so I will point you towards some ajax documentation, and the jQuery ajax documentation. You might also want to glance at the HTTP page on wikipedia just to get a better understanding of how the web works.