9

I'm trying to call a javascript function (actually coffeescript) from a controller in a Rails 3.2 app.

I'm getting a Render and/or redirect were called multiple times in this action error.

My code looks like this:

#Model.controller

def index
  @models = Model.all
  my_action if current_user.name == "Bob" #or some other general conditional
  ...and some stuff
  respond_to do |format|
    format.html
    format.js #this is needed to handle ajaxified pagination
  end
end

def my_action
  respond_to do |format|
    format.js { render :js => "my_function();" } #this is the second time format.js has been called in this controller! 
  end
end


#functions.js.coffee.erb

window.my_function = ->
  i = xy
  return something_amazing

What is the correct way to call a js function from the controller?

Andy Harvey
  • 12,333
  • 17
  • 93
  • 185
  • 1
    I think in coffee you need to declare a function like `my_function ->` (check this: http://js2coffee.org/ and try your coffee script, it does not work) – MrYoshiji May 22 '13 at 17:00
  • good spot, that one is a transcription mistake – Andy Harvey May 22 '13 at 17:09
  • I don't think my_function will be available as it isn't bound to the window or in the global namespace. See http://stackoverflow.com/questions/4214731/coffeescript-global-variables and http://stackoverflow.com/questions/9059475/how-to-generate-global-named-javascript-functions-in-coffeescript-for-google-a – Shawn Balestracci May 22 '13 at 17:22

1 Answers1

14

Man, you missed argument for block. Primary mistake.

def my_action
  #respond_to do # This line should be
  respond_to do |format|
    format.js { render :js => "my_function();" }
  end
end

And MrYoshiji's point is right. But your error was on server side, had not reached client side yet.

For the style, I think that's okay if the js code is one function call only. If more JS code, it's better to render js template

 # controller
 format.js

 # app/views/my_controller/my_action.js.erb
 my_function();
 // and some more functions.

Update: How to fix double rendering problem

You must have your #index return if condition met, or the method will continue to execute and cause rendering twice or more. Fix it like this:

def index
  @models = Model.all
  if current_user.name == "Bob"
    return my_action
  else
    # ...and some stuff
    respond_to do |format|
      format.html
      format.js #this is needed to handle ajaxified pagination
  end
end
Billy Chan
  • 24,625
  • 4
  • 52
  • 68
  • *faceplant*, good point. I've been playing with this too long! Problem not is that I have two blocks calling render (one in index, one in my-action) and this is throwing errors. – Andy Harvey May 22 '13 at 17:07
  • Thanks, this helps a lot and I think we're getting close to a solution. But I'm unable to resolve this `Render and/or redirect were called multiple times` errors. I've updated my question to provide more detail. Appreciate any ideas you may have – Andy Harvey May 23 '13 at 03:30
  • Thanks Billy, appreciate your quick response, and I apologize for maybe stupid questions while I try to understand this. I understand your solution above, but what if I want to add my_action to the functions already running on the page, not replace. For example, the javascript my_action function renders a popup only for Bob, but the rest of the page should still be rendered below this, not replaced. Thanks for helping me get to grips with this – Andy Harvey May 23 '13 at 03:45
  • @AndyHarvey, I see your points. If you want to do this, controller action is not right solution. You can simply use template to do that: <% if current_user.name == "bob" %> <%= javascript_tag bla blah %>. This is much easier and makes sense. – Billy Chan May 23 '13 at 04:03
  • hm yes, i thought of this but decided against it because it seems to go against the way javascript is handled in the asset pipeline, with all js loaded simultaneously. Perhaps I need to look at this again. So this is the way you would approach this? A js file that is not included in the application.js manifest, and a specific call to that js file in the view? It seems a bit clunky, surprised Rails doesn't have a "cleaner" way to handle this. – Andy Harvey May 23 '13 at 04:13
  • is it possible to create if statements directly in the javascript that use database variables? – Andy Harvey May 23 '13 at 04:15
  • Of course there are clean ways, that's unobtrusive JS. You ship html with correct markup and use JS to handle them, all separately. Your template is rendered by html, there is no need to call js in template directly, I just show the possibility. – Billy Chan May 23 '13 at 04:22