2

I have a view that has some dynamic hierarchical selects (that is, the first influences the options available in the second). Refreshing keeps the option of the first selected but erases the options from the second select. I handle this by having an onload that initializes the second select. Currently I have that window.onload in [controller name].js.coffee, but that onload runs with every page in my project. Obviously, those 2 selects aren't on every page. How do I add it just to the pages that need it?

Tyler
  • 1,818
  • 2
  • 13
  • 22

2 Answers2

2

For that, I set up the body ID for controller name and class for action name in the layout (application.html.*)

<body id="<%= controller_name %> class="<%= action_name %>">
  <%= yield %>
</body>

Then in your controller.js file, check for the desired controller and/or action;

$(document).ready(function() {
  if ($("#controller").length) {
    // Your specific controller code here
  }
})

Note for PJAX users: the body id will never updated, you have to hack into pjax middleware https://github.com/foohey/rack-pjax/commit/38caff53eec6a366aad3a2600d255529ebedfcc5#diff-2ae22cdf8106300731c4c37a3363ea48

m4tm4t
  • 2,361
  • 1
  • 21
  • 37
  • Is there any way to use this with partials? It's a good solution, but I have the body tag in application.html.* to eliminate redundancy, thus I can't add id or class to it. – Tyler Jan 30 '14 at 17:41
  • you should setup the body id in your layout (application.html.*) – m4tm4t Jan 30 '14 at 17:46
  • I never thought about the possibility there could be a universal way to access controller and action names from the view. Thanks. For anyone else doing this: [how to get controller/action names](http://stackoverflow.com/questions/3757491/can-i-get-the-name-of-the-current-controller-in-the-view) – Tyler Jan 30 '14 at 17:51
0

I built a solution on top of the recommendations of badfoo that uses classes to keep the namespace clean.

I added "controller" and "action" attributes to the body tag in the root layout (application.html.*).

Then, in application.js.coffee, I made an onload function that gets those attributes, checks if a class exists by the same name as the controller. (So if the controller was "Clients", it would look for a "clients" class in the js.) If the controller class has an onload method, it calls it first. The proceeds to look for an action specific onload as follows: It checks for a subclass of clients with the same name as the action. If it exists and has a method named "onload", it calls this method. If the subclass for the action doesn't exist, it checks for a method named "#{action}_onload" in the controller class and calls it.

window.onload = () ->
  capitalize = (str) ->
    str.charAt(0).toUpperCase() + str.slice(1)

  controller = capitalize(document.body.getAttribute('controller'))
  action = capitalize(document.body.getAttribute('action'))

  controller_class = eval "#{controller}"
  if controller_class?
    action_class = eval "#{controller}.prototype.#{action}"
    action_onload_func = eval "#{controller}.#{action}_onload"
    controller_onload_func = eval "#{controller}.onload"
    if action_class? and action_class.onload?
      onload_func = action_class.onload
    else if action_onload_func?
      onload_func = action_onload_func

  if controller_onload_func?
    controller_onload_func()
  if onload_func?
    onload_func()

It's important to note that these classes must be made global by prefixing them with an @ symbol.

Tyler
  • 1,818
  • 2
  • 13
  • 22