Rails Lesson #6: More AJAX

13 May 2008

In this lesson we’ll see how partials work and we’ll add a few more ajax niceties.

Partials are just .html.erb files that are useful for reusing “view” (as in Model-View-Controller) elements. They are easy to make and use – just add a “.html.erb” file to your view directory that starts with an underscore then call the render_partial method. Let’s make one for our recipe list items.

Create a new file “_recipe_list_item.html.erb” in the “app/views/recipes” folder. This is the contents of the new file:

<% recipe = recipe_list_item[:recipe] %>

<li id="recipe_<%= recipe.id %>">
    <%= link_to(recipe.name, cookbook_recipe_path(@cookbook,recipe))  %>

</li>

This grabs a recipe out of the rails-built hash and then renders a list item with a link to the recipe. Now we need to change our app/views/cookbooks/show.html.erb to use this partial.

Change this:

<ul id="recipe_list">
<% @cookbook.recipes.each do |recipe| %>
    <li>

        <%= link_to recipe.name, cookbook_recipe_path(@cookbook,recipe) %>
    </li>
<% end %>
</ul>

To this:

<ul id="recipe_list">

<% @cookbook.recipes.each do |recipe| %>
    <%= render_partial "/recipes/recipe_list_item",:recipe => recipe %>
<% end %>
</ul>

Go ahead and bring up a cookbook in your browser to ensure that nothing looks different. Now we need to change the create.rjs file that we made in the previous lesson to use this partial. In the last lesson I referred to the HTML in this file as “unsightly” because HTML doesn’t belong in an rjs file. I suppose there may be reasons for putting HTML in there but avoid it.

So, go ahead and replace this:

page.insert_html :bottom, "recipe_list",'<li>' + link_to(@recipe.name, cookbook_recipe_path(@cookbook,@recipe)) + '</li>'

with this that calls render_partial instead:

page.insert_html :bottom, "recipe_list",:partial=>'recipe_list_item',:object=>{:recipe=>@recipe}

While you have it open go ahead and add this line in there:

page["recipe_" + @recipe.id.to_s].highlight

Okay – now go ahead and reload your cookbooks page, then add a recipe. It works the same (only better because it highlights the new recipe after you add it!)

The only thing left to do now is make it so we can delete the recipes from our list– you probably added a bunch of terrible recipes to your cookbook. I know I did: “Strawberry Hashmelt”, “Boiled Beets”, “flashy flashy”, and of course: “sammy davis jr.” Nobody is going to want these recipes so we need a way to delete them.

Open up the recipelistitem partial and add a “remote” delete link:

<% recipe = recipe_list_item[:recipe] %>

<li id="recipe_<%= recipe.id %>">
    <%= link_to(recipe.name, cookbook_recipe_path(@cookbook,recipe))  %>
    <%= link_to_remote "delete", 
            :url => cookbook_recipe_path(@cookbook,recipe), 
            :confirm => 'Are you sure?', 
            :method => :delete %>

</li>

Because we are using a partial for the main display as well as the AJAX adds we didn’t have to add that code twice: hooray!

The linkto_remote tag will send a javascript request to the server. When the server receives a request via javascript we want to respond to it correctly. Open up your recipes controller and add a js format to the respondto block of the destroy action. Now just like we did for the create action we need to create an rjs file to define the behavior. The app/views/recipes/destroy.rjs file is very simple:

page["recipe_" + params[:id]].hide
flash.discard

Go ahead and delete some of your lesser recipes.

On your own

Below are some tasks you should try to do on your own. I’ll include my code for these next lesson when we get introduced to validations in rails.

  • Wrap the delete link in a div or span in order to make some space between the recipe name and the word delete.
  • Add an “edit” link to the list for quickly arriving to the edit screen. You should use a “cookbook_recipe_path” method.