Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby On Rails 3, form_tag remote on responding to Ajax request

thanks for reading this post. I've been stuck on an issue with RoR for the past few days. I have a form under index.html.erb as:

<head>
    <title>Ajax List Demo</title>
    <%= javascript_include_tag :defaults %>  
    <%= csrf_meta_tag %>  
</head>
<body>
  <h3>Add to list using Ajax</h3>

  <% form_tag :action => :list , :method=>:get, :remote=>true  do %>
    Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
    <%= submit_tag "Find" %>
  <% end %>

  <div id="my_list">
  </div>
</body>

In the controller I have:

 def list
    puts "here!!!!"
    reader = Reader.new
    @profiles =  reader.processURL(params[:url]) #profileList = 
    respond_to do |format|
      #format.html { render :partial=>true, :locals => { :profiles => @profiles}}#{          render :partial=>'profiles/list',:layout => false, :locals => { :profiles => @profiles}} 

format.js {render :content_type => 'text/javascript', :locals => { :profiles => @profiles}}

# index.html.erb
      #   format.rss     render :partial=>'profiles/list',:layout => false, :locals => { :profiles => @profiles}
    end

And a js file for remote UJS as list.js.erb

$("#my_list").html("<%= escape_javascript(render(:partial => "list"))%>");

The issue is I cannot get the results to render the partial _list.html.erb, in the div tag my_list. I get a blank page, 406 error. If I un-comment the render html code in the controller I get the partial back rendered in the browser. I am kind of stuck, I want to submit the form and the results to pop in the my_list div. I'm new to rails so if I'm missing something obvious don't hesitate to point it out to me....I'm definitely willing to try.


Changed it to this:

<html>
    <head>
        <title>Ajax List Demo</title>
        <h1>Listing posts</h1>
           <%= javascript_include_tag 'jquery.js' %>  
    <%= csrf_meta_tag %>  
    </head>
    <body>
        <h3>Add to list using Ajax</h3>

        <% form_tag :action => :doit , :method=>:get, :remote=>true  do %>
        Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
        <%= submit_tag "Find" %>
        <% end %>

        <div id="my_list">
                   </div>

Controller:

def doit
    puts "here!!!!"
    reader = Reader.new
    @profiles =  reader.processURL(params[:url])
    respond_to do |format|
 #     format.html {render :partial=>true, :locals => { :profiles => @profiles}}#{    render :partial=>'profiles/list',:layout => false, :locals => { :profiles => @profiles}} 
      format.js #{render :content_type => 'text/javascript', :locals => { :profiles => @profiles}}
      # index.html.erb
      #   format.rss     render :partial=>'profiles/list',:layout => false, :locals => { :profiles => @profiles}
    end

JS _doit.js.erb $("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");

And finally a partial:

_doit.html.erb.

However I am still getting the 406 error, I dont have a duplicate _doit js or erb. Does anything standout as incorrect from this? Thanks again!


Another update:

I think the form is not rendered correctly:

This rendered:

<% form_tag :action => :doit , :remote=>true, :id => 'myform' do %>
        Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
        <%= submit_tag "Find" %>
        <% end %>

This:

<form accept-charset="UTF-8" action="/home/doit?id=myform&amp;remote=true" method="post">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="&#x2713;" />
<input name="authenticity_token" type="hidden" value="MLuau4hvfdGO6FrYCzE0c0JzwHhHKZqjmV49U673sK8=" />
</div>        Enter the public url:
<input id="url" name="url" size="80" type="text" value="" />

    <input name="commit" type="submit" value="Find" />



        <input name="commit" type="submit" value="Find" />

Its adding my remote tag and id to the query string, isnt this wrong?

Ok finally got a clue forms need to be bracketed:

 <%= form_tag( { :action => 'doit' }, :multipart => true, :remote=>true, :id => 'myform' ) do %>

Ok last update tonight: Now I get in the logs:

Started POST "/home/doit" for 127.0.0.1 at Wed Oct 27 22:40:55 -0400 2010 here!!!! Processing by HomeController#doit as JS Parameters: {"commit"=>"Find", "url"=>"http://www.facebook.com/people/James-Stewart/653161299", "authenticity_token"=>"MLuau4hvf dGO6FrYCzE0c0JzwHhHKZqjmV49U673sK8=", "utf8"=>"Γ£ô"} Rendered home/_doit.html.erb (4.0ms) Rendered home/doit.js.erb (9.0ms) Completed 200 OK in 807ms (Views: 40.0ms | ActiveRecord: 0.0ms)

I see as JS and it says it renders my js/partial. However I am getting nothing on my_list div. My JS file:

$("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");

My html.erb form file has now:

<script$('#myform').bind('ajax:success', function(evt, data, status, xhr){
    xhr.responseText;
  });></script>

Its like the form does nothing, which is a good sign, no more 406 error. I know this is close, if anyone can point what I need to do in the js that would be great otherwise I'll take a break and try tmrw.


Ok I think its getting a response back just not rendering as you pointed out would be the issue yesterday Steve.

Debugging the JS on Firebug I see the html I want rendered in the div, for this:

http://localhost:3000/javascripts/prototype.js?1285674435/event/seq/1

Which means I think I am getting the JS response back now.

I have this on the form page:

<script>$('#myform').bind('ajax:success', function(evt, data, status, xhr){
    $('#my_list').html(eval(xhr.responseText));
  });</script>

Inspections say it doesnt know what myform is, but I put :id => 'myform' in the Rails code.


Again all thanks, I got a ton of help here and I want to share how I finally got it working back to the community.

The, js file for the method doit(def. need a better controller action name) is doit.js

The code was ultimately:

$("my_list").update("<%= escape_javascript(render(:partial => "doit"))%>");

For some reason leaving it as #my_list wouldn't be found in firefox, I had to use firebug to finally figure this out.

Obviously this is different from the way suggested below, and I am going to place the js script back into the form and remove the .js.erb file and see how that works works. I suppose I just render the partial in the format.js response? Also where does everyone find info on writing the UJS files? I know nothing about the syntax for anything starting with $.

Again thanks for the help, finally feel like I am making progress on learning rails.

like image 418
Kunal Avatar asked Oct 26 '10 03:10

Kunal


1 Answers

I posted this answer on Hacker News, but figured the Stack Overflow community might benefit as well :-)

In Rails 3, the javascript drivers are very hands-off (i.e. unobtrusive). The problem you're having is that your app is returning to the browser a string of javascript, but there is nothing in the page that is then executing that javascript in the context of the page.

The rails.js ujs driver binds to forms and links with data-remote=true, which is what the :remote => true is doing, to make them submit their requests remotely, but that is where the Rails magic stops.

The good news is that the remote requests fires off some events you can bind to, which give you access to the data returned by the server (which fire off in the following order):

  ajax:before
  ajax:loading
  ajax:success
  ajax:complete
  ajax:failure
  ajax:after

You need to bind an event to the ajax:success event of your form. So, if your form had the id "myform", you'd want something like this on your page:

  $('#myform').bind('ajax:success', function(evt, data, status, xhr){
    eval(xhr.responseText);
  });

xhr.responseText is what your server returns, so this simply executes it as javascript.

Of course, it's proper to also bind to the failure event with some error handling as well.

I usually don't even use the action.js.erb method of returning javascript, I just have my controller render the HTML partial, and then I have a binding like this in the page:

  $('#myform').bind('ajax:success', function(evt, data, status, xhr){
    $('#target-div').html(xhr.responseText);
  });

I'm actually in the middle of writing a full article on this, but hopefully this is enough to get you going.

EDIT: I finished that article, fully explaining remote links and forms in Rails 3. Maybe it will help:

Rails 3 Remote Links and Forms: A Definitive Guide

like image 150
jangosteve Avatar answered Nov 15 '22 17:11

jangosteve