How to use client side validation with remote => true rails 3.1 - ruby-on-rails

Normally we can use many ways to use ajax with validation like using validate plugin or using plain ajax(no rails ajax) like below
data using ajax requests
$.post("test.php", $("#testform").serialize());
In your situation it can be something like that.
$('#your_form').submit(function(event) {
event.preventDefault();
/* Email validation here.*/
$.ajax({
type: "POST",
url: $(this).attr('action'),
data: $(this).serialize(),
});
});
But, I wish to use client side validation with Real Rails ajax . Please help how to do this?

Maybe you'd like to use this gem
UPDATE 1:
Since you want to do this through ajax, validation can be handled through you controller.
# Let's assume your remote request hits index
def index
#object.create(params[:object])
respond_to do |format|
format.html # index.html.erb
format.js
end
end
Now, define an index.js.erb in the relevant views folder & render
<% if #object.new_record? %>
alert("Failed to upload record: <%= j #object.errors.full_messages.join(', ').html_safe %>");

Related

Rails is rendering an empty partial from js.erb

I'm trying to render a partial from a js.erb file but nothing is shown in the view.
There is an AJAX call that triggers the report_announcement action in the announcements_controller when a button is pressed.
reports.js
$.ajax({
type: "POST",
contentType: "application/json",
url: "/announcements/report_announcement.js",
data: json_data
});
reports/_report_content.html.erb
<div id="report_announcements_container"></div>
announcements_controller.rb
def report_announcement
#announcement = Announcement.new(announcement_params)
respond_to do |format|
if #announcement.save
format.js {}
end
end
end
announcements/report_announcement.js.erb
I know that announcements/report_announcement.js.erb is rendering ok because I can see the logging statement in the console and also in the development.log as well as the report_announcement object being logged to the console.
<% announcement_json = #announcement.to_json.html_safe %>
var report_announcement = <%= announcement_json %>;
console.log('report_announcement');
console.log(report_announcement);
// this is where something is not right I think
$("#report_announcements_container").html("<%= escape_javascript(render partial: 'announcements/report_announcements', locals: { announcement: #announcement }) %>");
console.log('inside announcements....');
announcements/report_announcements.html.erb
This is where I'm having an issue because the logging statement for the partial is shown however nothing from the partial is shown on the page. I see in the development.log that the page rendered but nothing is actually shown in the view. The partial is not showing on the page in the report_announcements_container div.
<h1>test</h1>
<%= announcement %>
<script>
console.log('inside partial...');
</script>
You're using the wrong content-type in your ajax call. Rails recognizes both application/javascript and text/javascript as the :js format.
Also when you request a js format you actually have to execute the returned javascript for anything to happen in the client.
$.ajax({
type: "POST",
contentType: "application/javascript",
url: "/announcements/report_announcement.js",
data: json_data
}).done(function(data){
jQuery.globalEval(data); // Yeah eval is evil.
});
If you have done any "real" ajax calls before this will seem completely backwards - thats because it is. js.erb is a poor mans ajax framework for the javascript impaired which consists of a bunch of routes and views that mutate the DOM in the client.
Manually creating calls for a js.erb template is just a silly excerise in masocism. If you really want to use it create a form and use it send the request instead so that you can just leverage the javascript handlers that rails provide:
<%= form_with(url: "/announcements/report_announcement", remote: true) do |f| %>
# ...
<% end %>

Displaying an error message without reloading the page in Rails

I have a page that lets one create records - if the validations aren't satisfied, it redirects to the same page and shows an error message. Here's that snip from the controller:
def create
#signature = Signature.new(signature_params)
if #signature.save
redirect_to "/thanks"
else
redirect_to :back, :notice => error_messages(#signature)
end
end
The trouble is, this is resulting in a full page refresh - so the error message isn't visible because the input form is placed under the fold of the page. I can place it at the top of the page, of course, but is there a way to show the message without reloading the page? Thanks.
OK, so here's what I've settled on:
1) I'm handling validation on the client side with HTML5 "required" attributes - they were created for this explicit purpose and no other gems or plugins are needed. They are supported in all major browsers. Details in this article.
2) I've moved the error messages to the top of the page to handle the case in which a user either is on an old or mobile browser or has JavaScript disabled. Error messages must work with a complete request-response cycle (even if this means re-loading the page) before they work with anything else - this is the unobtrusive JavaScript approach.
3) For the AJAX version, I'm going to be using remote: => true on the form element as explained in the Rails guides. I might be making this open source once I'm done with the callback part of it, and will post a link here.
Obviously, handling errors with flash is the most uniform & DRY way to show the user what's going on, but if you're willing to think outside the box, you'll be able to use Ajax to accomplish a similar job by just handling the errors yourself:
Code Example
#app/controllers/signatures_controller.rb
def create
#signature = Signature.new(signature_params)
if #signature.save
#success = "true"
end
respond_to do |format|
format.js { #errors = error_messages(#signature) }
format.html {
if #success.defined?
redirect_to "/thanks"
else
redirect_to :back, :notice => error_messages(#signature)
end
}
end
end
#app/views/signatures/create.js.erb
<% unless #success.defined? %>
alert(<%=j #errors.inspect() %>)
<% end %>
#app/assets/javascripts/signatures.js
$(document).on("submit", "#signature_form", function() {
$.ajax({
url: "/signatures"
type: "POST"
data: $(this).parent().serialize(); //serialize the form (not the button)
error: function() {
alert("Sorry, there was an error!");
}
});
});
You'd actually be better using JSON for this. If you like the idea, I can refactor it to include JSON for you!

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.

format.js is not manipulating dom once action caching enabled

Note: I am presenting a logic here what I am doing.
What I am doing:
Think about the basic index action where we are listing products and with pagination. Now using remote-true option I have enabled ajax based pagination. So far things works perfectly fine. Take a look on sample code.
Products Controller:
def index
#products = Product.paginate(:order =>"name ASC" ,:page => params[:page], :per_page => 14)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #products }
format.js
end
end
Index.html.erb
<h1>Products</h1>
<div id="products">
<%= render "products/products" %> // products partial is just basic html rendering
</div>
<script>
$(function(){
$('.pagination a').attr('data-remote', 'true')
});
</script>
Index.js.erb
jQuery('#products').html("<%= escape_javascript (render :partial => 'products/products' ) %>");
$('.pagination a').attr('data-remote', 'true');
So whats the problem:
Now I want to enable action caching on this. But index.js.erb file is not manipulating DOM. If I remove the remote-true functionality then things works fine with caching.
For action caching I have added this line on the top of the controller:
caches_action :index, :cache_path => Proc.new { |c| c.params }
Any suggestions?
Update:
Problem is jquery code is not executing. From this question
I found out what's wrong. jQuery actually surround the incoming script with a so that the browser evaluates the incoming code. But the caching mechansim merely saves the code as text and when one re-request, it returns the code as text but not evaluate it. Therefore, one needs to eval the code explicitly
But how to solve this problem??
After some trial and error, I think I have a work around.
Page links
Instead of
$(function() { $('.pagination a').attr('data-remote', 'true') });
use
$(function() {
$('.pagination a').click(function() {
$.ajax({
url: this.href,
dataType: 'script'
});
return false;
});
});
so response created by the app server will be run as javascript
Controller
next, change your caches_action line to
caches_action :index, cache_path: proc { |c| c.params.except(:_).merge(format: request.format) }
since ajax appends a _ params for some sort of timestamp
Hopefully this works :)
I don't see what the issue should be with using remote: true. Someone else suggested to use .ajax instead of remote: true, but that's exactly what the remote functionality does, so there shouldn't be any difference.
The other answer has code that explicitly uses jQuery.ajax, but the only difference in their code compared to what the remote functionality does is that they're specifying an explicit dataType. You can actually do that with remote: true though.
In your HTML link, you just need to specify data-type="script". Or, based on your posted JS, you'd do this:
$(function(){
$('.pagination a').attr('data-remote', 'true').attr('data-type', 'script');
});
EDIT: Also, I wrote more in-depth about the data-type attribute and how it works with Rails here: http://www.alfajango.com/blog/rails-3-remote-links-and-forms-data-type-with-jquery/
I think I found a solution to this problem. I have a controller that has caches_action for an action that uses format.js to fetch some data via ajax, and it was not working out of the box.
I found that, despite the request being transmitted to the server, and the server correctly parsing the request and "rendering" the index.js.erb template, nothing was updating in the DOM. Your solution with $.ajax and dataType:'script' fixed the problem for me, however, I didn't like having to do jquery to bind to a click on a link, which should happen by default... I was able to make it work correctly by changing my link_to to this:
= link_to "click me", user_action_path(params), remote: true, data:{type: 'script'}
Hope this helps!
I've been having the same problem but on my 3.0.3 application with :remote => true I have added :'data-type' =>: script and have been worked fine.
However, in my case, I don't see improvement when loaded by ajax the list.

Rails - Make ajax request from javascript event and load partial

what's up?
I'm using rails 3.2.3 with ruby 1.9.2p290.
I have a model Purchase which has many purchase_payments:
class Purchase < ActiveRecord::Base
has_many :purchase_payments, :class_name => 'PurchasePayment', :dependent => :destroy
accepts_nested_attributes_for :purchase_payments
def Purchase.build_payments(count)
#Creat #count payments with some logic for each of them
end
end
In my form, I have number_field:
<td>
<%= f.number_field :payments_count, :min => 0 %>
</td>
Now, I'd like to have the purchase_payment fields appear based on the number of payments. For that, I have a specific div:
<div id="purchase_payments_space"> </div>
Every resource I've found so far, talks about ajax generated by helper tags, like link_to ... , :remote => true, but using this tags, the user would have to click on the link to have the remote function called. In other part of my app, I use jquery ajax request and I'm trying to use like this for sending the request to my controller:
function load_payments() {
$.ajax({
type: "POST",
url: "/purchases/build_payments/" + document.getElementById("purchase_payments_count").value,
});
}
$(document).ready(function() {
$("#purchase_payments_count").change(load_payments);
}
In my controller, I have the respond_to :js right after my class declaration and the following method:
def build_payments
pc = params[:payments_count].to_i
#purchase = Purchase.new
#purchase_payments = Purchase.build_payments pc
respond_to do |format|
format.js
end
end
This way I have my app/views/purchases/buid_payments.js.erb run ok.
The thing is, I'm building a collection of payments that should be included in my form, and doing things the way I'm currently doing doesn't look like rails would do this. This way I will have to write much code in the js.erb file and still I can't figure out how I'll make the create method build the complete purchase from the params (I don't know how to build the form from the javascript).
I looked at several different ways but none have given an integrated solution for making an ajax request from a javascript event and have the view create the nested fields for my nested attributes (its a collection of purchase_payment I must remember).
I saw http://railscasts.com/episodes/196-nested-model-form-part-1 and http://railscasts.com/episodes/197-nested-model-form-part-2 but those build on top of helper methods like link_to and link_to_function.
Can anyone point me how can I accomplish that?
$.ajax({
url: "purchases/build_payments",
type: 'POST',
data: $('#purchase_payments_count').val(),
dataType: 'html',
headers: {
'X-CSRF-Token': '<%= form_authenticity_token.to_s %>'
}
});
then
def build_payments
pc = params[:payments_count].to_i
#purchase = Purchase.new
#purchase_payments = Purchase.build_payments pc
respond_to do |f|
f.js {}
end
end
which will run buid_payments.js.erb which can populate\recreate your table
If can't see it I can't help you.

Resources