Question

I am a complete newbie to the world of RoR.

Although I have generated some code using rails scaffold. I am also able to post a form via ajax using the :remote => true.

And the form gets posted, nice and clean! But I wish to render some data that was returned from my controller using link_to so that it creates a link to a route/resource.

I have tried creating a .js.erb file and embedding the code in it, but it gives me an error saying:

undefined method `link_to'

code in .js.erb

$(document).ready(function(){


$("#new_post").bind('ajax:success', function(xhr, data, status){ 
    console.log(data)
    if(data.errors){
        console.log("Error!")
    }
    else{
        var user_name = data.user; // json is contained in data variable        
        $("#recent_posts").prepend("<%= j link_to(user_name, '#') %>");
    }
});

});

Where am I going wrong?

Was it helpful?

Solution

I think there are several sources of errors here:

undefined method `link_to'

This is highly unusual, since link_to is a standard view helper. Are you sure the space before link_to is really a space and not some sort of other invisible char (you sometimes get those for example on OSX when you use space while holding alt). Furthermore, your js erb has to be in your controllers view namespace, but since it gets executed when you render the action, this seems to be the case.

$(document).ready(function(){

You won't need to use this in .js.erb templates, since there is no ready event fired. jQuery executes the script right away if the DOM is ready anyway, so it won't hurt (usually), it's just unnecessary.

$("#new_post").bind('ajax:success', function(xhr, data, status){

This is a bigger problem. The way you attach your handler now, it will be attached every time the form gets submitted and the server returns data. So, if there's an error, and the form gets submitted and returned a second time, you will either have the console message or the anchor insertion executed two times. So better be ready for this kind of event:

$("#new_post").not('.registered').addClass('registered').bind('ajax:success', function(xhr, data, status){

This will prevent multiple event handlers.

Then there's the third problem:

var user_name = data.user; // json is contained in data variable        
$("#recent_posts").prepend("<%= j link_to(user_name, '#') %>");

Here, you're trying to use a variable you assigned in javascript in the erb inline ruby. This won't work, since the ruby variable user_name is evaluated serverside when the template is rendered. The javascript variable assignment on the other hand is evaluated in the browser when the client receives the rendered template from the server. So, what can you do?

link_to is not all that magic. It just renders an anchor tag. So, the same can be written like this:

var user_name = data.user; // json is contained in data variable        
$("#recent_posts").prepend(["<a href='#'>", user_name, "</a>"].join(''));

So far, so good. The biggest problem you have though is: The server can not return both a rendered .js.erb template and json data at once. So the data you get in your javascript function will just be the rendered .js.erb template string. Communication with the server in JSON is really the best thing, so let's forget about the rails integrated form handling and write it ourselves:

  1. Delete :remote => true from the form options

  2. Add :format => :json to your create route

    resources :posts, :only => [:create], :format => :json
    resources :posts, :except => [:create]
    
  3. Add javascript in your assets that handles your form remotely

    // after DOM ready
    
    var form = $('#new_post');
    form.on('submit', function(e) {
    
      $.ajax({
        url: form.prop('action'),
        type: form.prop('method') || 'POST' //for create action,
        data: form.serializeArray(),
        dataType: form.data('type') || 'json',
        success: function(data, status, xhr) {
              var user_name = data.user; // json is contained in data variable        
              $("#recent_posts").prepend(["<a href='#'>", user_name, "</a>"].join(''));
        },
        error: function(xhr, status, error) {
          console.log('error')
        }
      });
    
      return false; //prevent normal submit
    
    });
    
  4. Now you're ready to return pure JSON from the server:

    render :json => resource.to_json(:include => :errors)
    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top