Conditional remote: true depending on validation - javascript

I have a form in a partial _donation_form.html.haml which is rendered as a modal. I am trying to do the following:
If the record can be saved, redirect to a different page
Else render the modal with validation error messages.
Everything works except that when the record is invalid, redirection to a different page does not work.
I have the following in _donation_form.html.haml which is rendered as a modal.
= bootstrap4_form_for([:admin, donation], remote: true, html: { multipart: true, id: 'manual_donations', autocomplete: 'nope'}) do |f|
# form with submit_button which calls `create` action of `donations_controller.rb`
This is my new.js.erb which renders the above modal:
$('#newDonation').modal('hide');
$('#newDonation, .modal-backdrop').remove();
$('body').append("<%= j render partial: 'donation_modal' %>");
$('#newDonation').modal('show')
I added remote: true in _donation_form.html.haml as when the validation fails I want to render new.js.erb.
Following is donations_controller.rb:
def index; end
def new
donation.cause_id = cause.id if entity.causes.count == 1
donation.event_id = params[:event_id]
end
def create
# some code
begin
ActiveRecord::Base.transaction do
donation.recorded_offline!
respond_with(:admin, donation, :location => admin_donations_path)
end
rescue ActiveRecord::RecordInvalid
render :new
end
end
When I click on submit button of the modal with a valid record, page does not redirect and I get the following message on console:
Processing by Admin::DonationsController#index as JS
-----------
No template found for Admin::DonationsController#index, rendering head :no_content
I have the view index.html.haml under app/views/admin/donations/. When I remove remote: true from _donation_form.html.haml, redirection works just fine. However, in that case rendering new.js.erb with validation error messages does not work. I am not really sure how to tackle this situation.

Related

Rails Controller - refresh page same as if I hit the refresh on my browser

I have inherited a Rails code base that I do not fully understand. We have a requirement to, when the user hits Submit, render ON THAT PAGE the set of validation failures the user put into the form. I cannot redirect to any other page - we must remain on the page which contains the form upon which they put the invalid input.
Here is my method
def tpr_bulk_update
updated, date_died = update_tprs #it returns 0,0 when there are validation fails
if updated == 0 && date_died == 0
flash.now[:notice] = 'A bunch of errors occurred'
#Here I need to refresh the page. I do not especially want to redirect.
#I want this to perform exactly the same as me hitting the refresh button on my browser.
#The initial form loaded via a very complicated codebase that I do not understand exactly.
#I do have available to me the params from the initial call - but it seems to me hitting refresh on
#the browser implicitly handles repassing to this method with the SAME PARAMS I CAME IN WITH....
#AND it shows my flash.now. So then, I want to refresh the page the same mechanism the browser uses,
#because this is what demonstrably meets my requirement
elsif !date_died
redirect_to tprs_index_vod_assets_path
else
flash[:notice] = "One or more TPR assets were not given valid future dates, so those invalid dates did not save".html_safe
redirect_to tprs_index_vod_assets_path
end
end
The issue is I see no way to do this. Perhaps the browser invoking refresh uses javascript that is impossible to inline in my rails controller?
redirect_to :back
fails on account that the set of params I came in with are not populated - it explodes.
respond_to do |format|
format.js {render inline: "location.reload();" }
end
My method does not output javascript, and neither will it ever output javascript - that is a requirement for the system.
I need whatever is equivalent to the refresh operation my browser (Chrome) performs when I press "Refresh". I want that to happen right after I set my flash.now message. How can I capture what Chrome/what hitting refresh actually does? And how can I perform it within my controller?
This is exactly what AJAX was designed for. By sending an asyncronous request you can send data to the server without reloading the page and update the page with the response. In Rails you can use Server Side Concerns to simply replace a chunk of contents with a rendered view.
Since you don't actually have an example of the form or controller this is a simplefied example that demonstrates the concept:
class ThingsController < ApplicationController
def new
#thing = Thing.new
end
def create
#thing = Thing.new(thing_params)
respond_to do |format|
if #thing.save
format.js
else
format.js { render :new }
end
end
end
end
end
# things/_form.html.erb
# remote: true is the default - its just added for extra clarity here
<%= form_with(model: #thing, remote: true, id: 'my-special-form') do |form| %>
<% if #thing.errors %>
# .. display the errors
<% end %>
# ...
<% end %>
# things/new.html.erb
<%= render partial: 'form' %>
// things/new.js.erb
// Since we want to extract the children of the form element
// we use DOMParser to create a fragment
const node = new DOMParser().parseFromString(
"<%= j render(partial: 'form') %>", // rails renders the template
"text/html"
);
// Find form on page and replace its contents
document.getElementById('my-special-form').innerHTML = node.querySelector('form').innerHTML;
// #todo set flash message
// things/create.js.erb
window.location = "<%= thing_path(#thing) %>";
The way that this works is that Rails UJS listens for submit events on any element with the data-remote attribute. Instead of the normal submit it will send an XHR request with the Content-Type: application/javascript header.
After rails is finished rendering and reponding with your js.erb view it is sent back to the client and Rails UJS takes the response and evals it by popping it into a script tag.

RoR 4 form_tag AJAX request issue

So I am trying to do an AJAX request in my form_tag. Basically, a user can enter a song name or an artist's name and I want to simply post what they searched for into a playlist (very simply here)
These are my files:
My form
<%= form_tag({:action => 'capp'}, method:'GET', class: 'ui form', remote: true , id: 'song_form') do %>
<div class="one field">
<div class="field" style="width: 100%">
<%= text_field_tag :query %>
</div>
</div>
<%= submit_tag("Search Song" , class: 'ui large green button') %>
<% end %>
Where I am trying to put the query entered
<div id="query"></div>
My 'app' Controller which has that action
class AppController < ApplicationController
respond_to :html, :js
def create
passcode = params[:passcode]
if passcode != nil && passcode.length > 1
redirect_to :controller => app, :action => capp, :passcode => passcode
end
end
def capp
#passcode = params[:passcode]
#query = params[:query] <-- I used pry and the query was being updated but the view itself was not.
if #query != nil
respond_to do |format|
format.html
format.js {}
end
end
end
end
My capp.js.erb file
addSongToPlaylist("<%= #query %>");
// For the record. I tried to also do this:
// addSongToPlaylist("<%= j render #query %>"); <-- But the app errored out saying render was an undefined method name...and all the online tutorials have their function rendering for some reason.
My capp.js file
function addSongToPlaylist(query) {
var c = "</br>" + query;
$("#query").append(c);
}
All I am hoping for is that the query I entered in the form above be placed in the
location below. But the as soon as I hit submit, the application doesn't do anything. the request goes through (I can see the server sending the GET request) but the view doesnt update. Please help!
I followed the tutorial on doing AJAX calls and for some reason it's still not working
Firstly, why aren't you using a path helper for your form_tag?
You'd benefit from using the likes of:
#config/routes.rb
get "/search/:query", to: "application#search", as: :search
This will allow you to use the following:
<%= form_tag search_path, remote: true, method: :get %>
<%= text_field_tag :query %>
<%= submit_tag "Search" %>
<% end %>
This will send a get request to your /search/:query path, which you'll then be able to process with your JS (explained in a second):
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
respond_to :html, :js
def search
#passcode = params[:passcode]
#query = params[:query]
end
end
This should handle the request for you, looking to open app/views/application/search.js.erb
--
JS
For sake of clarity, let's just put the function into your called JS file:
#app/views/application/search.js.erb
$("#query").append("<br> <%=j #query %>");
This will do what your addSongToPlaylist function is doing.
Considering you've populated the #query variable, and are passing the ajax properly, this should work.
Debug
One of the main problems you'll have is that you're not debugging your application.
In this instance, there are a number of potential issues which could prevent your Ajax from firing:
Your ajax isn't being passed (IE JQuery is not loaded)
Your path is incorrect
Your controller isn't handling the request properly
You're not handling the JS correctly
The way to initially test is to look at the network tab of your browser's developer console:
To access it, right-click, inspect element, then select network. This will give you a list of all the requests you've sent to your server, and their responses.
When you click your "form submit" button - check to see whether a new request is invoked. If not, it will generally mean you don't have jquery in your app (which is required by the Rails UJS).
--
Next, you need to check your Rails console to see if you're getting any requests. If not, it will not show anything coming through.
What you're expecting is to see a new set of commands triggered when you submit your "search" form. This will show you which controller#action has been triggered. If it's right, move forward.
--
Finally, you need to check your JS.
You can do this by using alerts:
#app/views/application/search.js.erb
alert("<%=j #query %>");
$("#query").append("<br> <%=j #query %>");
If the alert fires positively, you've got a hit.

js.erb not executing javascript but is processed rails

I'm trying to get my pagination working with Ajax, using either will_paginate or kaminari.
When I hit pagination link my log says
Processing by Instructor::FullDashboardController#pupil_leads as JS
and
Rendered instructor/full_dashboard/pupil_leads.js.erb within layouts/main (0.1ms)
But the js has no effect. So to test it I wrote
console.log("Hello");
<%logger.debug "This is the js file"%>
alert('Hello Rails');
In the js.erb file, in my log I see This is the js file as expected, but there is nothing in js console in my browser and the alert doesn't show. So I know the file is being processed, as my logger.debug works but there is no js.
How can I further troubleshoot this?
Rails 4.1
Ruby 2.1
Full Dash Controller
class Instructor::FullDashboardController < ApplicationController
layout 'main'
...
def pupil_leads
#ivar = Model.where("something = ?", some_object.id).order("created_at desc").page(params[:page]).per(10)
respond_to do |f|
f.js { render :content_type => 'text/javascript' }
f.html
end
end
Add layout: false option to the render block:
def pupil_leads
# some code here
respond_to do |f|
f.js { render layout: false, content_type: 'text/javascript' }
f.html
end
end
For some reason Rails don't recognize request as xhr, I also watched that the views extension (.erb.html or .slim) must be specified in full.

Issue with render or redirect_to in rails during ajax call

I am trying to redirect using ajax request(setting remote: :true for form) in rails. This is the issue i have.
In the controller, I have:
def create
user = User.new
user.attributes = params[:user]
if user.save
if request.xhr?
flash[:notice] = "User successfully created"
render js: %('#{user_url(user.id)}')
end
else
if request.xhr?
render partial: "form"
else
render action: 'new'
end
end
end
in model,
class user
validates :total_amount, presence: true, numericality: {greater_than: 0}
end
in the view, the javascript is:
$("#new_user").bind("ajax:success", function(evt, data){
if(data.length < 300){
window.location.replace(data.replace(/['\s]/gi, '')); # on success rendering the url.
}
else {
$("#errors").html($(data)); # if there are any errors, i have a div tag around form, which will replace the form html
}
});
When i click on submit(it is an ajax request) leaving total amount as 0.0 for the user(which is required), i get validation error which is the expected behavior (and the validation errors are displayed, this is the else part in the javascript).
Now i enter the amount as 1.0 and click on submit (this is an ajax request too). It is not going to the if part in the javascript.
What am i doing wrong here?
Instead of checking the length of the data, you should use ajax:error and ajax:success as two different callbacks. Error callback displaying errors, success redirecting.
Callbacks: https://github.com/rails/jquery-ujs/wiki/ajax

Input form where user could enter regular expression

I have a rails application in which I need to create some input form where user could enter regular expression. This regex needed to be passed to my method check_site(url, regex) that will return true or false depends on regex found on the page.
I've tried to create a 'link_to':
link_to 'Search', check(item.name, #pattern)
In this case method "check" is called not with button pressed but with page loading.
Do I need to use JS+AJAX? How to?
Crash course on getting AJAX/JS set up with your Rails form:
Ensure correct javascript is called in your page <head>: JQuery + application.js (Jquery-compatible version, of course!)
Change your link to:
link_to 'Search', check_item_path(:name=>item.name, :id=>#pattern, :var=>var, etc.), :remote => true
In items_controller:
def check
...your regex...
respond_to do |format|
format.html { redirect_to ...(wherever you'd like if no .js) }
format.js
end
end
Create a file called check.js.erb in the corresponding views folder and enter the javascript you'd like to update the page / confirm the form's successful submission / return results from your search, etc.
$(document).ready( function() {
$("#result").html(" <%= #item.result == true ? "true" : "false" %> ");
});
No method error: check_item_path
This means Rails is not recognizing the route to that controller action. Run rake routes in console and see if anything corresponds to the items controller, check_item action. If not, add:
match 'items/check_item'
to your routes file. Then run rake routes again and you should see check_item appear somewhere - if it does, you can use check_item_path again.

Categories