Section 5.3. View Templates


5.3. View Templates

Photo Share is supposed to be a web application for storing photos, but so far the scaffolding shows only boring filenames. To make that change, we'll work with view templates and controllers. Edit the file app/views/photos/show. rhtml , which is the view template created by the scaffold generator. If you have used template languages like ASP or JSP [*] before, you will recognize the syntax for embedding executable code within the HTML template. In this case, Rails is using the ERb (Embedded Ruby) template system for embedding Ruby code within an HTML template. As you recall, text between <% and %> is Ruby code that is executed, text between <%= and %> is a Ruby expression, and the results from executing that code is inserted into the HTML when ERb evaluates the template.

[*] ASP is Microsoft's Active Server Pages, and JSP is Sun's Java Server Pages: both are HTML template systems.

Insert this line at the beginning of app/views/photos/show.rhtml :

 <%= image_tag 'photos/' + @photo.filename %> 

This line calls the Rails helper function image_tag , which generates an HTML <img> tag for the photo's filename. By default, images are expected to be in the public/images directory of our Rails app, but the photos are in public/images/photos , so prefix the filename with photos/ . @photo contains the database record for the photos that we want to display and that was set by the photos controller:

 def show         @photo = Photo.find(params[:id])       end 

Let's see how this looks. Make sure that the web server is started, browse to http://127.0.0.1:3000/photos/list, and click on the Show link for any of the pictures. Now that (Figure 5-2) is much niceran actual picture!

Figure 5-2. Showing an actual photo

Now that you can see the images, it's time to go back and beautify the photo/list page. Do this by including the thumbnail image in place of the filename, and make it clickable, as a link to the show page. This strategy lets you eliminate almost everything else about the photo and enables the user go to the show page to see the details. Edit app/views/photos/list.rhtml to look like this:

 <h1>Listing photos</h1>     <table>     <% for photo in @photos %>       <tr>         <td>           <%= link_to(image_tag("photos/#{photo.thumbnail}",                                 :size => '75x56',                                 :border => 1),                       url_for(:action => 'show', :id => photo)                      )       %>         </td>         <td>           <%=h photo.filename %>           <br/>           <%= link_to 'delete me', { :action => 'destroy', :id => photo },                                      :confirm => 'Are you sure?' %>         </td>       </tr>     <% end %>     </table>     <%= link_to 'Previous page', { :page => @photo_pages.current.previous } if @photo_pages.current.previous %>     <%= link_to 'Next page', { :page => @photo_pages.current.next } if     @photo_pages.current.next %>     <br />     <%= link_to 'New photo', :action => 'new' %> 

There is a lot going on in this code, so we will go through it in considerable detail, but first, let's just see how it looks. Browse to http://127.0.0.1:3000/photos/list; you should see something like Figure 5-3. This is starting to look halfway decent.

Figure 5-3. Thumbnails in the photo list

Let's examine that code in detail:



<% for photo in @photos %>

Rails executes the code between <% and %> , looping through each database row contained in @photos , which contains a list of Photo objects set by the controller. Each Photo , in turn , is assigned to photo .



"photos/#{photo.thumbnail}"

Ruby allows single quotes and double quotes to delimit strings. Ruby evaluates the contents of strings with double quotes, but not single quotes. That evaluation pass will process substitutions. In this example, Ruby substitutes the result of photo.thumbnail , at execution time, for #{photo.thumbnail} , so this expression is exactly the same as 'photos/' + photo.thumbnail .



<%= link_to(image_tag(...), url_for(...)) %>

The link_to helper function creates a hyperlink. The first parameter is link text or the image to display, and the second parameter is the target URL for the link. [*]

[*] In Ruby, these parentheses are optional as long as the resulting code is not ambiguous. We include parentheses in this case because the parameters to link_to are themselves method calls.



image_tag("photos/#{photo.thumbnail}",

 :size => '75x56',                    :border => 1)> 

The image_tag helper function creates an image tag. The first parameter is the path to the thumbnail, and those remaining specify attributes for the image tag.



url_for(:action => 'show', :id => photo)

The url_for helper creates a URL that targets a given controller and action. Omit the controller, so that Rails defaults to the controller invoking the view. You need the ID of the photo to show, so use the photo object. Rails will substitute the ID of that object.



<%=h photo.filename %>

The h method creates properly escaped HTML text, so characters like < become &lt; . [ ] This line displays the photos filename, making sure that any special characters are properly escaped. We could have used <%= h(photo.filename) %> , but this style is more common because it makes the h call look more like its part of the tag.

[ ] When you include user-entered text from the database, you want escaped text because you dont know what characters it could contain. A user could accidentally or maliciously enter text that's interpreted as a database command. Malicious attacks that enter SQL commands into text boxes are called SQL injection attacks .





 <%= link_to 'delete me', { :action => 'destroy', :id => photo },                               :confirm => 'Are you sure?' %> 

Here the link_to method is used again. This time it creates a link to the destroy method of the current controller, but with a twist. We use the :confirm option, which creates a JavaScript pop-up dialog in the browser asking " Are you sure? " If the user answers "OK," the link is taken, and the photo entry is destroyed . If the user cancels, then nothing further happens.



<%= link_to 'Previous page', { :page => @photo_pages.current.previous }

Rails supplies pagination helpers to break long lists into multiple pages with Next and Previous buttons .



def list

@photo_pages, @photos = paginate :photos, :per_page => 10



end

This controller code, not shown in the example, calls the paginate method with two parameters. The first ( :photos ) says to read rows from the photos database table, and the second ( :per_page => 10 ) says to read these rows in groups of 10.

The paginate method returns two values: @photo_pages , which captures the current page while implementing next and previous, and @photos , which is the list of database rows (photos) for the page currently in view. You'll get a previous page if one exists.



if @photo_pages.current.previous %>

Our original code creates a link to the previous page of photos (using the @photo_pages object), but only if there actually is a previous page. Ruby will conditionally executes a line if you append an if expression at the end of the line.



<%= link_to 'New photo', :action => 'new' %>

You should be able to figure this one out by now. This creates a link to the new action in the current controller.

5.3.1. Layouts

You may have noticed that the HTML pages that we created are incomplete. Rails uses a feature called layouts to let you specify a common set of display elements for every page rendered by a controller. This feature is typically useful for common headers, footers, and sidebars. By default, Rails looks in its app/view/layouts directory for an RHTML file whose name matches the controller's name .

Take a look at app/views/layouts/photos.rhtml ; you should see something like this:

 <html>     <head>       <title>Photos: <%= controller.action_name %></title>       <%= stylesheet_link_tag 'scaffold' %>     </head>     <body>       <p style="color: green"><%= flash[:notice] %></p>       <%= @content_for_layout %>     </body>     </html> 

This is the layout template for photos controller. The HTML output created by any action in the photos controller is inserted into the layout where you see the line:

 <%= @content_for_layout %> 

and sent back to the browser for display. The end result is a valid HTML page.

Let's modify this layout to add some common links that will show at the bottom of every page. Edit app/views/layouts/photos.rhtml , and insert the following code just before the </body> tag:

 <div style="background-color:LightBlue"> <p>    &nbsp;   <%= link_to 'Photos', :controller => 'photos', :action => 'list' %>    &nbsp;   <%= link_to 'Categories', :controller => 'categories', :action => 'list' %>    &nbsp;   <%= link_to 'Slideshows', :controller => 'slideshows', :action => 'list' %> </p> </div> 

This layout displays a simple navigation bar with links to the pages that list the photos, categories, and slideshows. This navigation bar appears at the bottom of every page displayed by the photos controller.

Browse to http://127.0.0.1:3000/photos/list; you should see a web page like the one in Figure 5-4. Try clicking the new photo link or any of the thumbnails, and notice that the navigation remains at the bottom of the page.

Figure 5-4. Common navigation bar

This navigation bar is good, but it still has a few problems. First, it appears only when you are in the photos controller. If a user clicks on the Categories or Slideshows links, the navigation will be gone. You really want the same layout to appear throughout. Second, you should move the navigation bar to the top of the page so that it doesn't seem to jump around as users move from page to page.

By default, Rails looks for a layout file with the same name as the controller, which is why we added our navigation bar in the app/views/layouts/photos.rhtml file. You can also tell Rails what layout file to use. We'll do that, not directly in the various controller classes, but in the common parent class for all of the controllers.

The common superclass for all of the controllers is defined in app/controllers/application.rb . Edit this file and add layout 'standard' to the class body so that it looks like this:

 class ApplicationController < ActionController::Base   layout 'standard' end 

This tells Rails to use a layout named "standard" instead of the default name. And putting this in the superclass is the same as putting it in each controller individually. This approach is better, of course, because you don't have to duplicate the code, and if you add a new controller in the future, it will automatically use the same layout.

Now let's create the layout template. Create app/views/layouts/standard.rhtml with the following content:

 <html> <head>   <title>Photo Share</title>   <%= stylesheet_link_tag 'scaffold' %> </head> <body>   <div style="background-color:LightBlue">   <p>     &nbsp;     <%= link_to 'Photos', :controller => 'photos', :action => 'list' %>     &nbsp;     <%= link_to 'Categories', :controller => 'categories', :action => 'list' %>     &nbsp;     <%= link_to 'Slideshows', :controller => 'slideshows', :action => 'list' %>   </p>   </div>   <p style="color: green"><%= flash[:notice] %></p>   <%= @content_for_layout %> </body> </html> 

You probably recognize this text as pure HTML, with a few simple Ruby expressions to link to the list actions for photos, categories, and slideshows. This layout is the same as photos.rhtml , except that the navigation bar has been moved to the top of the page. Now you can click on any link, and every page in this Photo Share application will have this navigation bar at the top.

We no longer need the other layout files in app/views/layouts , so delete all of them, except for standard.rhtml .



Ruby on Rails[c] Up and Running
Ruby on Rails[c] Up and Running
ISBN: B003D3OGCY
EAN: N/A
Year: 2006
Pages: 94

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net