Jetset - a tutorial series

Friday, June 22, 2007 1:04 PM

When developing software, one of the trade-offs you have to face is between releasing early, and implementing more of the features you'd like to have. Releasing early allows you to learn from a much broader group of people; the down side is that if you release too early the product isn't useful enough, and making changes becomes harder since you have existing users to support.

The feedback we've received so far has been encouraging, and we're busy working on improvements and new features -- additional GData feeds, more UI controls, and enhancements to the editor, for example. However, we're also keen to start using the product ourselves; and so, in this blog I want to demonstrate a mashup that I've wanted to build for a while -- and, hopefully, start a discussion about what features might be useful to add to the framework, and how to work around things that are missing.

A Travel Mashup

A couple of weeks ago, I started planning a vacation (we say holiday where I'm from); I also wanted to visit a couple of overseas Google offices at the end of my trip. I've been doing quite a bit of flying (mostly between New York and Mountain View) over the last 12 months or so, and I don't have a single place where I can keep track of these trips -- so, I want to build a mashup to do this. Of course, I'd also like to be able to see my friends' trips, track expenses, search Google local, and keep my travel photos organized -- but first I need to get started...

To begin with, I'm going to create a list that contains my trips, which will be the primary data set for the mashup.
<gm:page title="JetSet" authenticate="true">
<h2>My Trips</h2>
<gm:list id="tripList" data="${user}/trips"/>
</gm:page>
Notice, that I'm using the ${user} query syntax to reference the private user data store for this mashup; following this, I've named the data set "trips". Since I'm going to need to log in to access this data, I've also had to set the authenticate attribute of the page to true.

By default, the list uses a built-in template that displays only a single text field per entry; I can easily extend this by declaring my own custom template, and setting the list's template attribute to reference it.
<gm:template id="tripTemplate">
<table style="width: 400px">
<tbody repeat="true">
<tr>
<td><gm:date ref="gd:when/@startTime"/></td>
<td width="100%"><gm:text ref="atom:title"/></td>
<td><gm:text ref="gmd:departure" size="3"/></td>
<td><gm:text ref="gmd:arrival" size="3"/></td>
<td align="right"><gm:editButtons/></td>
</tr>
</tbody>
</table>
<gm:create label="New Trip"/>
</gm:template>
Here, I've declared a text control (gm:text) that references the title element of each entry (ref="atom:title"), and a couple of other text controls that reference custom data fields ("gmd:departure" and "gmd:arrival"); I'm going to use the latter to input airport location codes (e.g., NYC, LAX, etc.) I've also added a date control, and most importantly, the gm:editButtons control that provides the edit and save buttons for each row. At the bottom, the gm:create control provides the "New Trip" button which fires the "create" event.

Now, any travel app isn't going to be complete without a map, so that's the next thing to add. We want the map to display the same data that is present in the list, so to do this, we can bind the map's data attribute to the list itself.
<gm:map id="tripMap" data="${tripList}"/>
The ${} syntax is used to reference either a built-in feed (e.g., ${user} as above), or the 'id' of another data-bound tag -- in this case, the list.

Finally, we want to wire things up so that when you click on the list, the corresponding map location is selected and vice versa. We can do this using event handlers.
<gm:list id="tripList" data="${user}/trips" template="tripTemplate">
<gm:handleEvent src="tripMap"/>
</gm:list>

<gm:map id="tripMap" data="${tripList}" create="true">
<gm:handleEvent src="tripList"/>
</gm:map>
Here, both the list and the map tags declare event handlers that "listen" to events from the corresponding tag. So, for example, when you select an item in the list, the "select" event is also processed by the map, and the opposite happens when you click on a pin within the map. Notice also, that I've added the "create" attribute to the map -- this displays the Create and Add Pin buttons on the map.

OK, we're ready to test the mashup now and start adding data. I've published my mashup at http://jetset.googlemashups.com so you can try it out and take a look at the complete source code. Click on the "New Trip" button to add a trip, then press "OK" and click on the map's "+" button to add a corresponding point on the map.

In my next post I'll start adding more features, but in the mean time let me know how you get on with your own mashups.