Inserting JavaScript into Templates

Problem

You want to insert raw JavaScript into a page and execute it as the result of a single Ajax call. For example, to enable users to print an article on your site, you want a "print" link that hides ad banners and navigation, prints the page, and then restores the page to its original state. All of this should happen from a single XMLHttpRequest.

Solution

Include the Prototype and script.aculo.us libraries in your layout, and define the positional layout of the different sections of your page:

app/views/layouts/news.rhtml:

<html>
 <head> 
 <title>News</title>
 <%= javascript_include_tag :defaults %>
 <style > 
 #news { 
 margin-left: 20px;
 width: 700px;
 }
 #mainContent { 
 float: right;
 width: 540px;
 }
 #leftNav { 
 float: left; 
 margin-top: 20px;
 width: 150px;
 }
 #footer { 
 clear: both; 
 text-align: center;
 }
 </style>
 </head> 
 <body> 
 <%= yield %>
 </body> 
</html>

The content of your page contains the article that is to be printed along with the un-printer friendly banner ad and site navigation. Include in this view a link to "Print Article" with the link_to_remote method:

app/views/news/index.rhtml:

<div id="news">
 <div id="header">
 <%= image_tag
 "http://m.2mdn.net/viewad/693790/Oct05_learninglab_4_728x90.gif" %>
 </div> 
 <div id="frame">
 <div id="maincontent">
 <h2>What Is Web 2.0</h2>
 <%= link_to_remote("Print Article", 
 :url =>{ :action => :print }) %><br />
 <p>September 2005. Born at a conference brainstorming
 session between O'Reilly and MediaLive International,
 the term "Web 2.0" has clearly taken hold, but there's
 still a huge amount of disagreement about just what Web
 2.0 means. Some people decrying it as a meaningless
 marketing buzzword, and others accepting it as the new
 conventional wisdom. I wrote this article in an attempt
 to clarify just what we mean by Web 2.0.</p>
 </div> 
 <div id="leftnav">
 <%= link_to "Home" %>;
 <%= link_to "LinuxDevCenter.com" %>;
 <%= link_to "MacDevCenter.com" %>;
 <%= link_to "ONJava.com" %>;
 <%= link_to "ONLamp.com" %>;
 <%= link_to "OpenP2P.com" %>;
 <%= link_to "Perl.com" %>;
 <%= link_to "XML.com" %>;
 </div> 
 </div> 
 <div id="footer">
 <br />
 (C) 2006, O'Reilly Media, Inc.
 </div>
</div> 

The NewsController sets up two actions: the default display action, and an action for printing. Neither of these methods need any additional functionality.

app/controllers/news_controller.rb:

class NewsController < ApplicationController
 def index
 end
 def print
 end end

The RJS template hides the elements that are to be omitted from printing, calls window.print(), and finally restores the hidden elements.

app/views/news/print.rjs:

page.hide 'header'
page.hide 'leftNav'
page.hide 'footer'
page.<<'javascript:window.print()'
page.show 'header'
page.show 'leftNav'
page.show 'footer'

Discussion

The RJS template in the solution produces an ordered sequence of JavaScript commands that hide unwanted elements of the page. While these elements are hidden, it prompts the user with the browser's native print dialog box. After the print dialog has been accepted (or canceled), the hidden elements are redisplayed.

The key to making the hidden elements reappear after the printer dialog has been cleared is the use of JavaScriptGenerator's << method. This method inserts the JavaScript directly into the page.

shows the news page before printing. The printable page is seen only in a possible print preview option of your print dialog.

Figure 8-6. A Print Article option created by inserting JavaScript via an RJS template

See Also