Yeast & AJAX

Yeast templates is especially suitable in order to develop AJAX based applications. In first place, the template data model is already designed in a format that makes easier the dynamic load of new and fresh data. In fact, with Yeast-AJAX the server and the browser do not interchange XML data. The same format used in the template designer's model may be used in subsequent interactions with the server to change the values of the model, or even to get extra values that were not initially loaded. Once loaded, new data may be evaluated (using the eval() JavaScript function), being at the template disposal as if they had belonged to the template from the beginning.

In second place, one of the more tedious tasks related to AJAX interactions, i.e. the modification of the document body accordingly to the new received data, can be easily performed using Yeast templates. In fact this modification is done by the same processing instructions that are used for the initial template rendering.

We could say that an AJAX interaction with Yeast templates simply consist of loading the new model section values and re-processing the affected elements.

Writing AJAX enabled templates

To develop an AJAX enable template is as easy as develop a normal Yeast template, apart from the following points:

  1. Create the designer's model taking into account not only the data needed initially, but also all the structures and values that may be received in subsequent AJAX interactions.
  2. Detect the template parts that must be re-processed in the AJAX interactions. Assign a unique identifier to those elements (with the id HTML attribute), and mark them using the yst attribute with the ajax or live value (yst='ajax' or yst='live')
  3. Write the handlers that recognize the events that trigger the AJAX interactions. In these handlers typically you must contact the server, load the new data using a XMLHttpRequest object, evaluate it and re-process the affected elements marked with yst="ajax" or yst="live". You can automate all that tasks using YST.AJAX.request(...) function.

You can read more about Yeast & AJAX in the Template Tutorial.

Example

You can test online this example, and others like it.

Let's consider one of the typical AJAX examples: a spelling suggestions service for text fields. While you type down the text field content, the template contacts the server under the scenes and loads a list of candidate words for the text field.

Yeast-AJAX example

The designer's model for this template is limited to an array for the list of candidate words.

<script yst="model">
  words = [...  candidate words ...];
</script>

Initially, when the template is first loaded, the words array is empty or even undefined. It is when the user begins typing on the text field when the array changes and becomes filled with candidate words.

The template body is very simple also:

<body>
<p class="head">Spelling  suggestions</p>

<form id="form1" name="form1"  method="post" action="">
 <table border="0">
  <tr>
   <td  class="normal">Beging typing any word: &nbsp;</td>
   <td  class="normal">
    <input  type="text" name="txt" value="" onKeyup="askServer(this.form)"/>
   </td>
  </tr>

  <tr>
   <td  class="normal">&nbsp;</td>
   <td  class="normal">
    <!--  This content will dynamically changed via AJAX  interaction -->
    <div yst="live" id="suggestions">
     <div yst="if" ystTest="!YST.Util.isEmptyArray('words')">
      <select yst="value" size="$words.length>10?10:words.length$"
              onChange="this.form.txt.value=this.value">
       <option yst="apply" ystSet="words" value="$e$">$e$</option>
      </select>
     </div>
    </div>
   </td>
  </tr>
 </table>
</form>
</body>

In the last listing we have highlighted in red the code that has to do with AJAX, and in blue the normal Yeast templates code. You can see that the select is generated using an apply processing instruction that uses the words array as ystSet. Since this array may be undefined or empty in the first load of the template we have "protected" the combo with a conditional instruction that test if the array is empty. Notice that the select size is adapted to the array size, but it is limited to 10.

The AJAX interaction is fired by the onKeyup event of the text-field. When the pressed key is released, the askServer function is invoked, causing the request to the server for candidate words, and the subsequent re-processing of the suggestions div element. Notice that this element is marked with a yst attribute with live value. This mark enables the div Yeast re-processing.

The askServer function uses a the YST.AJAX.request(...) function that automates the connection to the server, the new model evaluation and the Yeast element re-processing. The askServer body is as follows:

function askServer(f)
{
  YST.AJAX.request(
  {
    url:        'GetSuggestions',
    updateIds:  ['suggestions'],
    queryForm:  f,
    forceAbort: true,
    avoidCache: false,  // Use the browser cache
    preUpdate:  function(resp) { words = []; }
  }
  ); 
} 

In it, we contact to the GetSuggestions service, sending as http parameters the form elements (remember that the text-field where you type down the text is the only parameter). The browser cache is used, since two requests for suggestions for the same set of letters will have the same response (the English dictionary does not change so frequently). If you type down quickly, it is likely that a new request is done before the previous one hasn’t yet finished. In this case the previous request must be aborted. And finally, once the new data (of javascript type) is received and evaluated, the suggestions element is re-processed to show the list of received words.

In the server side

The code in the server side is very simple also.

import java.util.List;

import org.ystsrv.servlet.YSTContext;
import org.ystsrv.servlet.YSTServlet;

public class GetSuggestions extends YSTServlet {
  public void init() {
    Dictionary.getSuggestions("");    // It only loads and caches the dictionary
  }

  protected String handle(YSTContext context) {
    String txt = context.getRequestParameter("txt");
	
    List sug = Dictionary.getSuggestions(txt);
    context.toResponse("words", sug);
    
    return "AutoComp";
  }
}

To implement an AJAX service using Yeast-Server is so simple as developing a servlet class that extends org.ystsrv.servlet.YSTServlet.Yes, it is the same class as for non-AJAX Yeast servlets. When a YSTServlet receives a request that includes a special HTTP header (called X-Requested-With, with value XMLHttpRequest) it sends to the browser only the data the template needs. If the request does not include such a header, the YSTServlet sends the whole template. The X-Requested-With header is automatically included in the request by the YST.AJAX.request(...) method of the Yeast engine (like other AJAX APIs, such as jQuery, do). Another option, that behaves the same, is to include an special CGI parameter, called yst.ajax with value 1, in the request. Obviously, to return the data for a certain AJAX interaction, you can use simple servlets instead of YSTServlets.

In this case the servlet GetSuggestions reads the txt parameter, which contains the text the user has typed down. Then GetSuggestions uses the Dictionary class to retrieve a list of words that begin with that text, and return them to the client. E.g., the response for a request like GetSuggestions?txt=templ (or GetSuggestions?yst.ajax=1&txt=templ) is:

words=['templar','template','templates','temple','templed','temples','templet','templets'];

You can read more details about YSTServlet in the Yeast-Server Manual.