Tapestry and AJAX: Autocompleter and InlineEditBox

Although the term "AJAX" was coined not so long ago, AJAX-like functionality has already been available in Tapestry for quite some time. In Tapestry 4.0 we can use Tacos, a rich and interesting library of custom Tapestry components that includes a number of AJAX-driven components. Tapestry 4.1 has Dojo JavaScript library built into it, so it comes with some core AJAX-enabled components.


A downloadable zip file is available for this article.

The essence of AJAX is that we don’t need to request the whole page from the server every time we want to change it. Instead, the page contains some clever dynamic things in it (with Tapestry these things are AJAX-enabled components) that can communicate with the server behind the scenes and redisplay themselves with new information quickly and smoothly in some clever way.

The benefit of Tapestry AJAX components is that you can use AJAX without any knowledge of JavaScript whatsoever. Components encapsulate everything required for their work, and you only need to configure their required bindings. Well, in fact there is one additional chore when dealing with AJAX components: you have to make JavaScript libraries available to the page. For this, you’ll have to use Shell and Body components in the same way as we did it for DatePicker in one of the previous articles.

I decided to introduce one of Tapestry’s AJAX components now because Autocompleter is quite similar to the PropertySelection component we dealt with in the previous article. It has the same two required bindings, model and value, and its model is an implementation of a special interface with a few methods for us to implement. So it shouldn’t be too difficult.

The idea is to propose to the users of the CelebrityCollector application an alternative way of selecting a Celebrity of the Week. We don’t want to keep them desperately searching a long list of celebrities trying to find their favorite. Instead, they will enter the first few characters of their favorite’s surname into the Autocompleter, and this clever component, after an invisible rendezvous with the server, will suggest a list of available surnames starting from the entered characters. Here is the result we want to achieve:

As usual, let’s begin with a mock up. Here is the old form for selecting a Celebrity of the Week:

<form action="" jwcid="@Form">

  <select jwcid="selectCelebrity">

    <option value="1">Angelina Jolie</option>

    <option value="2">Bill Gates</option>

  </select>

  <input type="submit" value="Save"/>

</form>

Change it to become

<form action="" jwcid="@Form">

  <select jwcid="selectCelebrity">

    <option value="1">Angelina Jolie</option>

    <option value="2">Bill Gates</option>

  </select><br/><br/>

  <input type="text" jwcid="auto"/><br/><br/>

  <input type="submit" value="Save"/>

</form>

The addition is very simple: we use a text box as a placeholder for the future Autocompleter, and it is marked as a Tapestry component. The next step is to configure this component in the page specification:

<component id="auto" type="Autocompleter">

  <binding name="model" value="autoModel"/>

  <binding name="value" value="celebrityOfTheWeek"/>

</component>

It looks very similar to PropertySelection, doesn’t it? It connects straight to the ASO, in the same way as the PropertySelection does, but its model should implement a different interface, the IAutocompleteModel.

{mospagebreak title=The IAutocompleteModel interface}

Create a new Java class, named, say, AutoModel, let it implement the IAutocompleteModel interface and then allow NetBeans to automatically create all the required methods (follow the steps outlined in the previous article for the IPropertySelectionModel). As a result, you should have something similar to this:

package com.devshed.tapestry.celebrities;

 

import java.util.ArrayList;

import java.util.List;

import org.apache.tapestry.dojo.form.IAutocompleteModel;

 

public class AutoModel implements IAutocompleteModel {

 

  public AutoModel() {

  }

 

  public String getLabelFor(Object obj) {

    return null;

  }

 

  public List getValues(String input) {

    return null;

  }

 

  public Object getPrimaryKey(Object obj) {

    return null;

  }

 

  public Object getValue(Object id) {

    return null;

  }

 

}

First of all, let’s pass to the model a list of available Celebrity objects in its constructor, in the same way we did this for the IPropertySelectionModel. The only difference is that I decided to use the Java 5 coding style in this example:

private List<Celebrity> celebrities;

 

public AutoModel(List<Celebrity> celebrities) {

  this.celebrities = celebrities;

}

Now let’s look at the methods. The first one, getLabelFor(), is self-descriptive. It accepts an object from the displayed list, in this case a Celebrity object, and should return a string to be displayed for this object by the Autocompleter. Let’s just return the celebrity’s surname:

public String getLabelFor(Object obj) {

  Celebrity c = (Celebrity) obj;

  return c.getLastName();

}

The next method, getValues(), is probably the most interesting, and the most Autocompleter-specific. When the user enters the first few characters of the surname, Autocompleter needs to make a request to the server behind the scenes, passing the entered characters and asking for the server to return all celebrities that match the input. In other words, the Autocompleter will ask the server to filter all available celebrities according to some criteria. Which criteria? This is exactly what we define in the getValues() method. The server will invoke this method on the Autocompleter’s model and return whatever the method returns. Let’s have a look at the completed method first and then discuss what it does:

public List getValues(String input) {

  List<Celebrity> list = new ArrayList<Celebrity>();

  for (Celebrity c : celebrities) {

    if (c.getLastName().toLowerCase().startsWith(

               input.toLowerCase())) {

      list.add(c);

    }

  }

  return list;

}

First of all, we are preparing a new empty list to be returned by the server:

List<Celebrity> list = new ArrayList<Celebrity>();

Than we take all the available celebrities and iterate through them using that convenient new Java 5 version of the for operator:

for (Celebrity c : celebrities) {

  …

}

On each iteration we are checking whether the current celebrity’s surname starts with the characters entered by the user. We are converting both the surname and the entered characters to lower case, so it doesn’t matter which case was used for input. If the surname matches, we are adding that celebrity to the returned list:

if (c.getLastName().toLowerCase().startsWith(input.toLowerCase())) {

list.add(c);

}

The remaining two methods are simple. The getPrimaryKey() should return some unique identifier for the given Celebrity object. As Celebrity has an id property, we can simply return it:

public Object getPrimaryKey(Object obj) {

  Celebrity c = (Celebrity) obj;

  return c.getId();

}

Note that the id is of the primitive int type while the method returns an Object; that’s fine from the Java 5 point of view as it will use autoboxing – i.e. automatically convert int into Integer.

Finally, in the getValue() method we are asked to find an appropriate Celebrity object by the given unique identifier. We are simply iterating through the available celebrities, and as soon as one’s id matches the method’s parameter, we are returning that object:

public Object getValue(Object id) {

  Integer i = (Integer) id;

 

  for (Celebrity c : celebrities) {

    if (c.getId() == i.intValue()) return c;

  }

 

  return null;

}

The last step in getting Autocompleter ready is to provide to it a model in the CelebritiesList class:

public IAutocompleteModel getAutoModel() {

  return new AutoModel(getCelebrities());

}

{mospagebreak title=Enabling JavaScript}

Before running the application, don’t forget to provide the required support for JavaScript, otherwise Autocompleter won’t work:

1. Turn the <html> element of the Web page into a Shell component. This will ensure that the required JavaScript libraries are available for the page. In the current version of Tapestry we also need to disable debugging (I would say it should be disabled by default, but it isn’t), otherwise Dojo will print ugly error messages straight across the page. Here is how everything should look:

<html jwcid="@Shell" title="Celebrity Collector: Celebrities List" debugEnabled="false">

2. Turn the <body> element into the Body component. This will show Tapestry where to place any JavaScript it might want to write for us:

<body jwcid="@Body">

3. Mark the existing <DOCTYPE> and <head> elements for removal as they will be added by the Shell component and we don’t want them to be duplicated:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

    jwcid="$remove$">

  <head jwcid="$remove$">

    …

  </head>

Now, the moment of truth has arrived. Run the application and navigate to the page with the table of celebrities. You will see that the text box that we’ve used in the mock up became something different, something which looks like a drop-down list:

Try typing the character g into this fancy control (well, that’s because we have two celebrities with surnames starting with "G") and you should see how Autocompleter works. It’s AJAX in action!

{mospagebreak title=Meet InlineEditBox}

Now we have some experience with Autocompleter, one of Tapestry’s AJAX components. I hope you will agree that it wasn’t too difficult to deal with — at least, it wasn’t more difficult than the PropertySelection component.

Now, as a reward for your perseverance, I want to show you another AJAX component, InlineEditBox. It provides a fascinating bit of functionality, and it is also unbelievably easy to configure.

Do you remember that Details page where we were displaying detailed information about a celebrity? Say we want to allow users to edit that information. This functionality might not fit the whole logic of the existing application quite nicely, so consider this as a diversion designed just to show off the InlineTextBox component.

Say we want to allow users to edit the occupation of the displayed celebrity. This is how we are displaying the occupation now:

<tr>

  <td><b>Occupation:</b></td>

  <td><span jwcid="occupation">Actor</span></td>

</tr>

And here is how the component is currently configured in the page specification:

<component id="occupation" type="Insert">

  <binding name="value" value="celebrity.occupation"/>

</component>

To enable editing, all we need to do is replace the Insert component with the InlineTextBox component. And since the latter has exactly the same required binding as the former, all we need to do is change the type of the component:

<component id="occupation" type="InlineEditBox">

  <binding name="value" value="celebrity.occupation"/>

</component>

It’s as simple as that!

Okay, there is one other thing to do. You already know that to enable AJAX functionality you have to make JavaScript stuff available at the page, but this is easy. Go on and do those few little changes to <html>, <body> and the other tags of the Details page as outlined above for the CelebritiesList page (you don’t need to use the debugEnabled="false" attribute though).

You will also need to remove the currently used Insert component from the title of the page, as Tapestry will not be happy seeing a component inside of the removed block of markup (the <head>). Let the title be just

<title>Celebrity Collector</title>

Then remove the fullName2 component from the page specification and rename the jwcid="fullName2" reference in the page template into just jwcid="fullName". You can use the source code provided in the zip file at the beginning of this article to make sure everything is correct.

Now run the application, navigate to the Details page, and at first glance you won’t notice anything different. However, if you hover your mouse pointer over the value for the occupation, in this case "Programmer ;)", you’ll notice that the page background slightly changes its color:

Now, click on the word, and you’ll see a magical change happening:

The word can now be edited! Make some changes and press the Save button. The occupation changes accordingly. And you can be sure that the change was invisibly reported to the server, and stored in the property of the page class bound to this control. Isn’t this magic? Especially considering the amount of effort (close to zero) required to invoke all this rich functionality!

The two components we have just learned to use, Autocompleter and InlineEditBox, might be the most useful of all the Dojo-powered components, and at the same time they are so easy to use. But Tapestry has more components in this group: a Dialog, to display a fancy modal dialog, a DropdownDatePicker and a DropdownTimePicker, the purpose of which should be clear from their names. There is also a magical EventListener annotation that allows you to connect a wide range of possible events in the user’s browser with listener methods of the page class, thus opening fantastic opportunities for us developers… but let me leave all these treasures until a later article.

For now, I am going to return to my earlier plans and demonstrate one of the most impressive features of Tapestry, from the point of view of everyday Web development: the ease with which you can create and add to your toolbox just any custom component you are able to imagine. This will be the topic of the next article.

See you later.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye