Rails form submission not happening in the background - ruby-on-rails

I have a Rails 5.2 app, where I'm using form_with, which is generating this HTML:
<form id="post-0" class="" action="/posts.js" accept-charset="UTF-8" data-remote="true" method="post">
The form is submitting successfully to the PostController's create action.
def create
#post = current_user.account.posts.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to posts_path, notice: 'Post added.' }
format.js { render :create, status: :created }
else
format.html { render :index }
format.js { render :create, status: :unprocessable_entity }
end
end
end
It is then rendering the JS view:
app/views/posts/create.js.erb
alert('created');
However, in my browser, I'm literally redirected to http://localhost:3000/posts.js where it shows the javascript code. Meaning, the form submit doesn't seem to be happening in the background.
I would have expected to still have stayed on the page the form was on, with a Javascript Alert appearing saying "created".
Is there's something silly I've missed? Seems to me it must be something with form_with that I'm not doing right.
I tried swapping it for form_for #post, remote: true, format: :js do ... but that had the same problem.
At this point, I can't tell if the problem is in View that shows the form, or in the Controller's create action. The controller is rendering create.js.erb and the URL shown is http://localhost:3000/posts.js which does seem to suggest it's a Form problem in the View.
This app is Rails 5.2. I'm not sure it's relevant, but I've removed the Asset Pipeline for Webpacker, I do have rails-ujs installed (as it is showing the confirm dialog in these link_to links as expected <%= link_to('Destroy', user, method: :delete, data: { confirm: 'Are you sure?' }) %>

Form submissions can occur locally (ie. not remote) due to a number of reasons.
Some common reasons local submission may be occurring when using form_with include:
local: true
The local attribute is optional for form_with. If not provided, form_with will by default set local: false.
true will set the form to be submitted locally (standard form submission), not remote/asynchronously.
false will set the form to be submitted remotely/asynchronously.
Any value other than false (including :false which is a symbol not a boolean) will be interpreted as the value being true (ie. submit locally).
Javascript triggered form submissions
Despite the setting of the local attribute being set to something that should trigger a remote/asynchronous form submission, the form may still be submitting locally if you're triggering the form submit event via Javascript.
In the example given in question, the HTML generated by the form_with use is as expected, so attributes provided to form_with are not the problem in this instance.
If javascript is being used to submit the form, that could be the problem. For example, even with data-remote="true" on the form, this javascript will be a problem:
document.getElementById('form-0').submit();
This submits the form, but doesn't raise a submit event for rails-ujs to intercept via an onsubmit event handler.
Working with the Rails Unobtrusive Javascript Adapter
To have the form submission triggered by javascript (whether you're using jQuery, Stimulus JS, or anything else) you need to use a mechanism that rails-ujs (or jquery-ujs) will intercept for you.
Rails provides the Rails.fire custom javascript event wrapper in rails-ujs, which can by used like this:
Rails.fire(document.getElementById('form-0'), 'submit');

You're doing a very basic mistake. By using ajax you can't use format.js. This Ruby Module is used to redirect the page. instead you should be using render json: { status: 200}

saw your job on Upwork. No need to spend $100 on this!
<%= form_with url: content_export_path, method: :post, id: 'ce_form1', data: { remote: true } %>
Look where the remote: true declaration is... (inside data hash)
Let me know if this works.

Related

How should a form error page be rendered with Turbolinks 5

Using the new Turbolinks 5 in a rails application - what is the best way to render a form with error messages. The documentation says:
Instead of submitting forms normally, submit them with XHR. In response to an XHR submit on the server, return JavaScript that performs a Turbolinks.visit to be evaluated by the browser.
So if my form submits a remote request to update should i be just doing a js form replace or does turbolinks 5 have a better way? Example -
controller:
def update
#success = #team.update_attributes( team_params )
end
update.js
<% if #success %>
Turbolinks.visit('<%= teams_path %>', {action: 'replace'});
<% else %>
$('form').replaceWith('<%= j(render partial: '/teams/form') %>');
<% end %>
Is there a more turbolinks 5 way to handle the failed update?
I'm still tinkering around as well. The only thing I'd be doing differently is put the conditional logic in the controller instead of the view.
somethings_controller.rb
def create
if #something.save
redirect_to #something
else
render 'errors'
end
end
errors.js.erb
$('form').replaceWith('<%= j(render partial: 'form') %>');
Why does the controller-based redirect work? Because in the docs for Turbolinks 5 it says...
The Turbolinks Rails engine performs this optimization automatically
for non-GET XHR requests that redirect with the redirect_to helper.
...which I take to mean they're basically wrapping our redirect in a Turbolinks.visit for us. The nice thing is that it'll degrade gracefully for users without javascript without us having to futz with respond_to logic.
You can see why redirect_to works by checking out the gem: /gems/turbolinks-5.0.0.beta2/lib/turbolinks/redirection.rb
Turbolinks doesn't handle form submissions yet so you need to handle the submission and response explicitly (via remote: true or otherwise). There's a discussion about it here with some other solutions: https://github.com/turbolinks/turbolinks/issues/85

Rails + ajax, my "edit.js.erb" is not being run

I'm trying to setup ajax within rails, but I am having trouble getting my "edit.js.erb" to be ran
My edit.html.haml
= f.check_box :foo, {checked: eligibility_data.foo, class: "foo", remote: true}
controller
def edit
#survey = Foo.find_by_id(params[:id])
respond_to do |format|
format.html
format.js
end
end
edit.js.erb
alert("Testing");
How can I get edit.js.erb to run? Once it's working I want to append html to my view, will I be able to place that html right into my js.erb, or will I have to make a partial of some sort?
You can't append remote: true to a checkbox and expect it to fire off.
It needs to either be attached to the parent form tag of said checkbox, or you need to write some js that watches that checkbox.
If you go the watcher route, the JS will send an ajax call when the box gets clicked on.
Here is a link on how to do AJAX with jQuery (which is built into rails)

Respond with javascript to an HTML request?

Is it possible to respond to an HTML request with javascript? By this I mean that I don't want the page to be refreshed and plus execute some javascript code. I wonder if this is possible by changing only the server side?
In my action, I have something selected to respond to js and html like this:
respond_to do |format|
format.js
format.html
end
And I have a .js.erb file that, of course, should be rendered when the browser requests a javascript.
Can this be done? How?
You cannot do this by only changing the server side code. If you'd like javascript to be executed upon the server's response, then the form will need to be submitted through javascript. This is because javascript will eval the response from the server and run the code.
This can somewhat be trivially added in Rails:
<%= form_for #user, remote: true %>
When it is submitted, then you can send back javascript. For example, to alert that a user was added:
/app/views/users/create.js.erb
alert("user <%= j #user.email %> was added");

Rails with jQuery + AJAX fundamentals

I've been given a task to do a simple Task Manager on Ruby On Rails. It's pretty simple for me but there is one issue. Everything has to be "ajaxified" and data should be passed around in JSON.
How I do this right now.
On index.html.erb
I'm creating a simple form with these parameters:
<%= form_for(Task.new, remote: true, :html => {:'data-type' => 'json'}) do |f| %>
In TasksController:
class TasksController < ApplicationController
respond_to :json
def create
#project = Project.find(params[:task][:project_id])
if #task = #project.tasks.create!(name: params[:task][:name], description: params[:task][:description])
respond_with(#task)
else
flash[:error] = 'error'
end
and inside on index.htm.erb I have:
$(document).ready(function(){
$("#new_task").bind('ajax:success', function(evt, data, status, xhr){
var row = ('<tr id="task.id"><td>'+data.name+'</td>'+'<td>'+data.description+'</td>'+'<td>'+data.state+'</td>'+'<td><%= link_to "delete task", task_path(task.id), :method => "delete", remote:true, data: { confirm: "Are you sure?" } %></td></tr>');
$(row).insertAfter("#tasks_table tr:first");
slicer($("#total"));
slicer($("#active"));
}); });
$("#new_task").bind("ajax:beforeSend", function(event,xhr,status){
$(this)[0].reset();
});
And now I'd like to give some explanation about how this might work(my personal thoughts)
by specyfing remote: true, I'm telling the form to submit data in input on server via Unobtrusive Javascript, rails 3 feature. It sends an usual hash in my example this is params[:task][:name] etc, but it expects to get back JSON because I did set :html => {:'data-type' => 'json'}) (is this correct syntax?). Now in my TasksController class I have the respond_to :json, it means that controller WILL answer those requests, where data-type json is specified, with json, because respond_with is smart enough to do to_json on object you are doing respond_with.
Am I right in all those assumptions?
So the question is if we can use js.erb and json.erb(yes?) do you need to return the json at all ?
Maybe going a step back helps.
With AJAX form, the submit event goes through JavaScript first, and does not trigger the browser to invoke a POST '/data.json'.
If you look at the HTML that is generated by Rails with the remote: true option, you probably see a "onsubmit()" or similar callback. It is also the JavaScript, that processes a 'success' or 'error' event. I like the jQuery documentation for this: http://api.jquery.com/jQuery.ajax/
Your question is also somewhat opinionated, because JavaScript libraries such as Backbone.js, Ember.js or Angular, allow to fine-tune the process chain of a form submit even further.

Rails - How do I get my remote form to respond with JS?

I have a page that list items. Below each item it lists out any comments for that item
(comments is a nested resource under items).
You can add a new comment in-line on the items page. When you do that, it was reloading the whole page so I want to change it to use Ajax and just use jQuery to insert the comment.
So I changed my comments/_form.html.erb to use :remote=>true
<%= form_for([#item,#comment], :remote=>true) do |f| %>
I added a format.js in my comments_controller:
def create
#comment = Comment.new(params[:comment])
#comment.save
respond_to do |format|
format.js
format.html { redirect_to items_path}
end
end
And created a simple comments/create.js.erb to insert the new comment onto the page:
$("#comments").append("<%= j(render(#comment)) %>");
However, when I submit a comment (even with the :remote=>true on the form) it keeps reloading the whole page/ignoring my .js file.
So I deleted format.html from the respond_to (since I don't want to use that option), but then it gives me the error Completed 406 Not Acceptable in 4ms (ActiveRecord: 0.2ms)
How can I get it to stay on the current page and respond with my create.js?
I just found the problem:
I was submitting my comments form with javascript: this.form.submit(). That worked fine when I wasn't using :remote=>true. However for some reason breaks when I make the form :remote=>true.
If I use an actual submit button, the code in my question works fine.
OR if I change my JavaScript from this.form.submit() to using jQuery to select the form by id and submit that, that works too:
$("#new_comment_<%= #item.id %>").submit();
I came across this question when trying to figure out something similar in 2020, using Rails 6.
I have a form created with form_with, which is remote: true by default, and I was submitting it in a Stimulus.js controller using the submit() function on the HTMLFormElement. This didn't work, but using a <input type="submit"> button instead worked, just like in the original question.
The reason behind this is that calling submit() in JavaScript doesn't trigger the onsubmit event on the form, while clicking a <input type="submit"> button does trigger this event (MDN docs). Rails' handling of remote: true depends on this event being fired, so the behaviour breaks.
I used the workaround from this answer to submit my form in JavaScript, and the remote: true behaviour now works as expected.
Also, if you're using rails-ujs, you can use this wrapper to submit the form (source):
form = document.querySelector('form');
Rails.fire(form, 'submit');
I hope this helps someone else!
You should add :format => :js onto your form_for
And possibly update,
format.js { render :nothing => true }

Resources