rc3.org

Strong opinions, weakly held

Ruby on Rails layouts and JavaScript includes

Ruby on Rails includes the concept of layouts, which enable you to specify that outer framework that pages in your application will be rendered within. Most view libraries support includes, which enable you to do things like this:

My page

Do stuff here.

Layouts are a bit nicer in that you don’t even have to maintain the includes on each page. One nice feature of layouts is that if you create a variable within a page, it will be accessible when the enclosing layout is rendered. So you can have a layout like this:

<%= @page_title %> <%= yield :layout %>

And then a page like this:

<% @page_title = "My Web Page">

<%= @page_title %>

This is my web page.

The page title variable that’s set on the page will be visible to the layout, enabling you to use titles specific to each page even though they’re all rendered in the same layout.

Now to get to my point. What’s the best (or most accepted) way to link to include page-specific JavaScript in a Ruby on Rails application? Script references belong in the page header, not in the page body. The page header lives in my layout file. I could just embed the script within the specific pages, but that’s cheating, and I don’t like to cheat.

The solution I came up with involves creating an external JavaScript file for every page that has its own scripts. Rails provides a helper method called javascript_include_tag that builds a link tag to a named JavaScript file, as long as it’s in the public/javascripts directory in your application. So to include public/javascripts/application.js, you just use:

<%= javascript_include_tag "application" %>

Right now, I’m taking advantage of this feature along with the way variable scoping works with layouts and views to handle page-specific Javascript. In my layout file, I have the following:

<% if @page_specific_scripts_in %> <%= javascript_include_tag @page_specific_scripts_in %> <% end %>

In my view, I have the following:

<% @page_specific_scripts_in = "controller_index" %>

And then in public/javascripts I have controller_index.js. This works, but I can’t help but wonder if there’s a better way.

10 Comments

  1. Couldn’t you have conditional logic within the head of the layout template that tests for a string length greater than 0 for a variable that is identified in the page (like the page title is)?

  2. Couldn’t you just use content_for and yield, as described here:

    http://errtheblog.com/post/28

    Eg:

    In the layout/view

    Then in the header

    The best part is you can have that yield in every header/layout without having to set it in every view.

  3. Ach, the filter took out my examples. Just imagine regular erb tags around my code.

    In the layout/ view:

    % content for :javascript do % % javscript_include_tag ‘myjsfile’ % % end %

    Then in the header

    % yield :javascript %

  4. I like that “content for” approach. I think that’s probably the right answer.

  5. Script references belong in the page header, not in the page body.

    Not if you have a high-traffic site and you want to keep the site speed high.

    The problem is that whenever a browser hits a script block, it goes into synchronous mode, which causes the page to take longer before content shows up. Placing the script blocks at the end of the page allows the browser to render the entire page (and display the content) before it flips to synchronous mode and slows down. The user’s impression is that the site is faster.

    Pages that use JS for formatting, ‘document.write’, etc. are not going to be able to do this, and that’s a good argument to a) use CSS instead and b) rethink the view layer architecture with an eye of eliminating document.writes — are they absolutely necessary, or just “nice to have”?

  6. I’m a little late to the game, but in the controller/action you could define…

    @page_javascript_includes = [‘script1’, ‘script2’]

    Then in the view

    % @page_javascript_includes.each do |script| % %= javascript_include_tag script % %end %

  7. Hi 2 all… Thanx in adv… Iam new to Ruby On Rails… i want to develop web application using Ruby which contains Headers,Fotters and popups… iam unable to call Headers, Fotters and popups in index page… Plz anybody help me out 1) where shud i save all these pages 2) how to call these pages into index page 3) is there need to write controllers… plz

  8. Sorry I am very late on this. However what if you change to be set in the controller much like you do layouts.

    So then you would perhaps have a scripts folder in your app directory that follows the same structure as views.

    So an example would be: class WelcomeController < ApplicationController

      def index
                script "index"
      end
    

    end

    or class WelcomeController < ApplicationController

      def index
                script!
      end
    

    end

    This would then look for say an rjs or js file in app/scripts/welcome/index.rjs.erb or app/scripts/welcome/index.js

    I like what you have done, its very cool. However I feel that what I am recommending is much cleaner perhaps. It could be extended to where you can add system wide scripts on the application controller say: class ApplicationController < ActionController::Base scripts [:default, :jquery, :myhelpers] end

    and then they would go into say app/scripts/{here} or app/scripts/application/{here} or even stay where they are now in public/javascripts/{here}

    Anyway just my two cents.

  9. I am however not sure if adding the scripts in the way I suggest breaks out of the MVC pattern. The script being basically just for the view and has no association with controller logic.

    Keeping it purely just in the views is perhaps architecturally better.

  10. nice! i was struggling with this for a while! thanks

Leave a Reply

Your email address will not be published.

*

© 2024 rc3.org

Theme by Anders NorenUp ↑