Chapter 9. REST and Ajax: Taking things further

image with no caption

It’s time to consolidate your mash-up skills.

So far you’ve seen how you can add Google Maps to your web apps to clearly show spatial data. But what if you want to extend the functionality that’s already there? Keep reading, and we’ll show you how you can add more advanced Ajax goodness to your mash-ups. And what’s more, you’ll learn a bit more about REST along the way.

Too many incidents!

With the improved user interface, the number of visitors to the Head First Climbers site has soared. The trouble is, so many incidents are being logged that there are too many for people to easily read through them.

image with no caption

The index page of the site displays the information in two ways.

  1. At the top of the page is a detailed list of incidents with latitudes and longitudes. The trouble is, lots of people scroll past this to get to the map at the end of the page.

  2. On a map showing a cut-down amount of detail when you click on an incident. The problem here is not all the data is shown on the map.

Neither of these are entirely satisfactory. It’s hard to locate the incidents from the list, and that’s why we added a map. But the map doesn’t display all of the data available. So what should we do?

The map could show more details

The ideal solution would be to make the map do more. If it could be changed to display more useful information about the incidents, we could probably just remove the the list of incidents and make the front page one big multi-functional map. That would mean we wouldn’t need to go to separate pages in order to enter more data, for example.

image with no caption

There’s just one problem: the map partial was downloaded. It’s simple enough to use, but should we really change the code? Fortunately, there’s a development technique the map partial uses that means we don’t need to touch the downloaded code itself. But what is it?

Brain Power

How do you think we could go about making changes to the map without changing the downloaded code?

We can extend the map using Ajax

The people who wrote the map partial figured that pretty soon people would want to extend the way the map worked. And because the map partial calls Google Maps, and Google Maps is built using Ajax, it made sense that the way to extend the map is with Ajax.

At the moment, the map works by making a request back to the server asking for an XML file containing the details of all of the mountaineering incidents recorded on the system. By default, the map displays the title and description contained in the XML.

But the map partial also allows you to pass it the name of an action that will display the information for an incident. And that action can be whatever you like, so if you wanted, you could generate something that looked like the original version of the incident show page.

image with no caption

We need the _map.html.erb partial to make Ajax requests, so this means it will need access to the Prototype library. We can allow this in the same way we did before, by adding a reference to the JavaScript library in the layout file like this:

image with no caption

But how do we convert the index page?

The first thing we need to do to change the front page is remove the list of incidents and make the map larger:

image with no caption

We also need to tell the map partial the name of the action that will display the incident information. That’s quite a few changes, but it will actually make the index.html.erb template a lot simpler than it was before.

In fact, this is all we’ll be left with:

image with no caption

When the map partial is called like this, it changes its behavior. Before, when an incident was clicked, the partial ran a piece of default JavaScript that displayed the title and description in the pop-up information window. Making this change means that when an incident is clicked on the map, the partial calls the show action and displays the action’s response in the window.

Or at least it will, once we make the show action generate the correct output.

What will the “show” action need to generate?

We already have a show action, and this generates a web page containing the details of an incident and a map with the incident’s location.

But that’s way more than we need now. The show action only needs to generate the text details of an incident, and because the information is going to be displayed next to a point on a map, we won’t need to display the latitude and longitude either.

There’s one other thing that’s different: we only need a page fragment. We don’t want the standard HTML boilerplate that will be produced by a page template. So this means our action will need to generated from a partial template. We’ll call this _show.html.erb.

image with no caption

The new map functionality is a success!

The new map functionality is greeted enthusiastically by the climbers. They no longer need to scroll past a long list of incidents to get to the map. Now they can get all the information they need direct from the map itself. There’s just one thing...

image with no caption

Climbers want to report new incidents using the map.

They can use GPS units to find their latitude and longitude and type that in, but the site would be a lot easier for them if they could simply plot a point on the map and fill in the other details right there. That would make data-entry quicker for the climbers.

So how does entering data on the map compare with what we’re currently doing?

We need to create requests using Ajax, too

If someone wants to create a new incident report, they currently have to go through the following steps:

  1. Click on the New Incident link on the front page.

    You can’t enter data directly on the front page, you need to follow a link to the “new” page instead.

    image with no caption
  2. Manually enter the latitude and longitude on the New page.

    There’s no map on this page, so you need to enter the latitude and longitude manually and save the record.

    image with no caption
  3. The incident you’ve created is displayed.

    Once you’ve clicked on the save button, you’re to the cut-down “show” page we’ve just created. And if you need to create a second incident or get back to the main map, you need to hit the back button a couple of times to the front page—where you begin all over again.

    image with no caption

So what needs to change?

Rather than go through all these steps, the users want the interface to be much simpler. They just want to click on the map and fill in the details using a form in the pop-up window.

image with no caption

To make that change, we need to generate the “new” form using Ajax. We also need the map to call the form when someone clicks on a new spot. But how?

The map partial lets us specify a “new” action

So far we’ve looked at showing the incident details on the map. But how do we go about creating new incidents?

The _map.html.erb partial lets us specify an action to handle new incidents, in the same way it lets us specify an action for showing incident details. This means that we can add a “new” action to the index.html.erb file like this:

image with no caption

If someone clicks a new spot on the map, the _map.html.erb will create a new marker and pop up an information window containing whatever is returned by the “new” action.

We already have a “new” action defined for the application, but it’s generating a full web page. Rather than display a full web page, we need the “new” action to create a page fragment that will be displayed inside the pop-up information window. Also, we need to make sure that when the user submits the “new” form, it stays on the map.

So we’ll create a partial called _new.html.erb that generates an Ajax form.

image with no caption

The climbers are confused.

Even though the form works fine and incidents are being saved to the database, it actually looks like nothing is happening. When the user clicks on the “Create” button, there’s no feedback indicating the record was saved. This means that the users are repeatedly hitting the Create button, and the database is starting to get lots of duplicate records.

Something needs to be done. Back in the old days when we just had scaffolding, when a user reported an incident with the “new” page, the browser would immediately switch to the “show” page. This confirmed that the data was saved to the database.

image with no caption

Could we have something like that in the Ajax app?

How do we PROVE an incident was saved?

The system really needs to show the created record in the pop-up window using the “show” action. So if someone enters details of an incident, the pop-up should change to show a read-only version of the incident.

image with no caption

That way, the pop-up information window will work like a little browser, forwarding to the new information. Except of course, our code can’t just forward the browser to the new information. We need to keep climbers on the same page... just with new information showing.

Brain Power

The Ajax form needs to be replaced with the contents of the “show” action for the incident. How do you think you could do that?

The form needs to update the contents of the pop-up’s <div>

Even though it looks almost like a desktop application, Google Maps basically boils down to just HTML and JavaScript. It’s just a web page. That means that the pop-up information window—and everything else—are just pieces of HTML.

The contents of the pop-up window are defined in a <div> element with:

id='map_info'

This is important because we’re using an Ajax form to create a new incident report, and Ajax forms can be used to dynamically update parts of a web page using their ids.

image with no caption

So if we can get the form to update the map_info <div> with the contents of the “show” action for the incident, it should give the users the feedback they need.

Avalanche!

So far we’ve allowed users to see more detailed information on the map, and also create new incidents onto it direct. But what about edits?

image with no caption

How things works now...

There are two places where scaffolding gives you an edit option.

On the original scaffolded version of the “index” page, you could click the “Edit” link next to any of the records and jump to the edit form. But we can’t do anything like that now because the list of incidents on the index page has been replaced by the map. And we know that the map partial doesn’t have any “Edit” functions built in.

image with no caption

So where else could we edit things in the original scaffolding? Well, another place is in the incident “show” page. In the scaffolded version of the application, there was an “Edit” link on the “show” page.

image with no caption

So could we do something like that? How about if we add an “Edit” link to the set of details that are displayed in the pop-up window.

Would that work?

We could have an “Edit” link in the pop-up

We need to add an “edit” link to the details that appear when someone selects an incident from the map. When someone clicks on the “edit” link, we switch the contents of map_info <div> to display an edit form and then use that to amend the record.

image with no caption

We already have the “show” function built. The “edit” form should be similar to the “new” form, and we already have the back-end code in the controller to amend the record.

How hard can it be?

We’ll start by modifying the “edit” action

We need some way of generating an edit form that will appear in the pop-up window. We’ll create a partial called _edit.html.erb. The partial looks pretty similar to the _new.html.erb:

image with no caption

So why have two partials?

The two partials are basically the same code, but it’s a good idea to keep them separate. At the moment they both look the same, but that might not always be the case.

As an example, we might want to change what functionality is available through the “new” and “edit” pages. We might decide that we need users to insert the date of an incident, but we may want to stop them editing it afterwards. Another possibility is that we might want to make the two pages look different from each other.

image with no caption

Rails knows if the form’s model object has been previously saved to the database.

So the code that the form_for helper generates changes depending upon whether it is dealing with an unsaved object (in which case, the form will call the “create” action) or an object that’s already been saved in the past (in which case, the form will call the “update” action).

Talking of actions, as well as creating the partial, we need to modify the “edit” action method in the controller to return the “edit” partial when it’s called:

def edit
  @incident = Incident.find(params[:id])
  render :partial=>'edit', :locals=>{:incident=>@incident}
end

We should now be able to generate an edit form, but how will the user get to it? We need an “Edit” link to appear when the user looks at the details of the incident. The link will need to be added to the _show.html.erb partial.

image with no caption

To generate the link, we’ll use the link_to helper.

So how do we use the link_to helper?

The link_to helper takes two parameters: the name of the text in the link and the place we’re going to.

<p><%= link_to "Edit", "/incidents/#{incident.id}/edit" %></p>
image with no caption

That’s a very good point.

So far we have created lots of paths and URLs using strings. But what if we change the format of the links in the future? We can fix things in the routes pretty quickly, but we will have a lot of redundant strings in the code containing paths.

Having the same sort of information in two places is bad because it breaks an important Rails principle:

Didn’t we say this before?

Note

Don’t Repeat Yourself

But if the routes already record the structure of the paths and URLs, maybe it’s worth looking at routes in a little more detail.

The “new” form we created earlier was able to replace the contents of the pop-up information window because it made an Ajax call to the server. It used the server response to replace the contents of the map_info <div>.

image with no caption

But the link we just added didn’t do that. It just told the browser to link to another page. If we create an Ajax link instead of a browser link, we can get around the problem.

An Ajax link works a lot like an Ajax form. When you click on an Ajax link, it doesn’t tell the browser to go to a different page, instead it generates an Ajax request to the server and uses the response to update part of the page. If this sounds familiar, it’s because Ajax links are almost identical to the Ajax buttons we used earlier on to refresh the seating list at Coconut Airways.

To convert the link into an Ajax link, we have to change this:

<p><%= link_to "Edit", edit_incident_url(incident) %></p>

to this:

image with no caption

The link should now generate an edit form from the server and display it in the pop-up window. Let’s see how it works now.

We’re using the wrong route!

When Rails receives the Ajax request from the link, the Ajax link sends out a request correctly to:

http://localhost:3000/incidents/5/edit

Instead of matching the request to the edit_incident route, it matches to one of the default routes:

image with no caption

Rails tries to match it to the default route near the bottom, and it sets the :action parameter to ‘5’ and the :id parameter to ‘edit’. There’s no action called ‘5’, so it fails.

But how can that be? Our URL (http://localhost:3000/incidents/5/edit) is the same path format as the edit_incident route (/incidents/:id/edit). So why didn’t it match? After all, the link worked fine before we converted it to Ajax.

Brain Power

Look at the list of routes again. The original link and the Ajax link are both going to the same URL. Why do you think the Ajax link was matched to the wrong route?

The HTTP method affects the route that’s chosen

There’s one column in the routes that we’ve not really looked at:

image with no caption

So what are those GET, POST, PUT and DELETE words about?

They are the HTTP methods—also called the HTTP verbs. Every request uses a particular HTTP method, and Rails uses the method as well as the path to decide which route to use.

But what exactly are they?

So what’s an HTTP method?

Despite the name, HTTP methods are really nothing like the Ruby methods you find in, say, a controller. Instead, an HTTP method is mentioned in the low-level HTTP-talk that happens when a client contacts the server:

image with no caption

So why did the two versions of the link do different things? Well - ordinary HTML hyperlinks send GET requests to the server. But, by default, Ajax links send POST requests.

So to make the link work, we also need to tell the link what HTTP method to use like this:

<p><%= link_to_remote "Edit", :update => "map_info",
        :url=>edit_incident_url(incident), :method=>'get' %></p>

Head First Climbers needs you!

The mountaineers are loving the application. But we think you can do a better job.

It’s time to lay your thing down and seriously pimp this application. Add more details, add more widgets, add more eye candy. Here are some ideas:

image with no caption

Build your best version of the Head First Climbers app, then submit your URL in the Head First Labs “Head First Rails” forum. You stand the chance of winning a bunch of O’Reilly goodness and the World Wide Fame of being featured on Head First Labs!

Note

Visit us here for details on how to enter the contest.

image with no caption

Tools for your Rails Toolbox

You’ve got Chapter 9 under your belt, and now you’ve added the ability to add more advanced Rails functionality to your web apps.

Rails Tools

rake routes displays the routes for the application

<route name>_path(object) returns the path for the given named path, using the id from the given object

edit_<model_name>_path(object) returns the path to the editor for the object

new_<model_name>_path returns the path to the new editor

Changing “_path” to “_url” returns a full URL instead of just the local path

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.73.35