What if you wanted to update an element in the page, not with JavaScript as in Listing 11-5, but with a PHP script executed by the server? This would give you the opportunity to change part of the page according to a server response. The remote_function() helper does exactly that, as demonstrated in Listing 11-8.

Listing 11-8 - Using the remote_function() Helper

<div id="myzone"></div>
<?php echo javascript_tag(
  remote_function(array(
    'update'  => 'myzone',
    'url'     => 'mymodule/myaction',
  ))
) ?>

Note The url parameter can contain either an internal URI (module/action?key1=value1&...) or a routing rule name, just as in a regular url_for().

When called, this script will update the element of id myzone with the response or the request of the mymodule/myaction action. This kind of interaction is called Ajax, and it's the heart of highly interactive web applications. Here is how Wikipedia (http://en.wikipedia.org/wiki/AJAX) describes it:

Ajax makes web pages feel more responsive by exchanging small amounts of data with the server behind the scenes, so that the entire web page does not have to be reloaded each time the user makes a change. This is meant to increase the web page's interactivity, speed, and usability.

Ajax relies on XMLHttpRequest, a JavaScript object that behaves like a hidden frame, which you can update from a server request and reuse to manipulate the rest of your web page. This object is quite low level, and different browsers deal with it in different ways, so handling Ajax requests manually usually means writing long portions of code. Fortunately, Prototype encapsulates all the code necessary to deal with Ajax and provides a simpler Ajax object, and symfony relies on this object. This is why the Prototype library is automatically loaded once you use an Ajax helper in a template.

Caution The Ajax helpers won't work if the URL of the remote action doesn't belong to the same domain as the current page. This restriction exists for security reasons, and relies on browsers limitations that cannot be bypassed.

An Ajax interaction is made up of three parts: a caller (a link, a button, a form, a clock, or any control that the user manipulates to launch the action), a server action, and a zone in the page to display the response of the action. You can build more complex interactions if the remote action returns data to be processed by a javascript function on the client side. Symfony provides multiple helpers to insert Ajax interaction in your templates, all containing the word remote in their name. They also share a common syntax — an associative array with all the Ajax parameters in it. Be aware that the Ajax helpers output HTML code, not JavaScript.

Ajax links form a large share of the Ajax interactions available in Web 2.0 applications. The link_to_remote() helper outputs a link that calls, not surprisingly, a remote function. The syntax is very similar to that of link_to() (except that the second parameter is the associative array of Ajax options), as shown in Listing 11-9.

Listing 11-9 - Ajax Link with the link_to_remote() Helper

<div id="feedback"></div>
<?php echo link_to_remote('Delete this post', array(
    'update' => 'feedback',
    'url'    => 'post/delete?id='.$post->getId(),
)) ?>

In this example, clicking the 'Delete this post' link will issue a call to the post/delete action in the background. The response returned by the server will appear in the element of id feedback. This process is illustrated in Figure 11-1.

Triggering a remote update with a hyperlink

Figure 11.1 Triggering a remote update with a hyperlink

You can use an image instead of a string to bear the link, use a rule name instead of an internal module/action URL, and add options to the <a> tag in a third argument, as shown in Listing 11-10.

Listing 11-10 - Options of the link_to_remote() Helper

<div id="emails"></div>
<?php echo link_to_remote(image_tag('refresh'), array(
    'update' => 'emails',
    'url'    => '@list_emails',
), array(
    'class'  => 'ajax_link',
)) ?>

11.3.2. Ajax-Driven Forms

Web forms typically call another action, but this causes the whole page to be refreshed. The correspondence of the link_to_function() for a form would be that the form submission only updates an element in the page with the server response. This is what the form_remote_tag() helper does, and its syntax is demonstrated in Listing 11-11.

Listing 11-11 - Ajax Form with the form_remote_tag() Helper

<div id="item_list"></div>
<?php echo form_remote_tag(array(
    'update'   => 'item_list',
    'url'      => 'item/add',
)) ?>
  <label for="item">Item:</label>
  <?php echo input_tag('item') ?>
  <?php echo submit_tag('Add') ?>
</form>

A form_remote_tag() opens a <form>, just like the regular form_tag() helper. Submitting this form will issue a POST request to the item/add action in the background, with the item field as a request parameter. The response will replace the contents of the item_list element, as illustrated in Figure 11-2. Close an Ajax form with a regular </form> closing tag.

Triggering a remote update with a form

Figure 11.2 Triggering a remote update with a form

Caution Ajax forms can't be multipart. This is a limitation of the XMLHttpRequest object. This means you can't handle file uploads via an Ajax form. There are workarounds though — for instance, using a hidden iframe instead of an XMLHttpRequest.

If you want to allow a form to work in both page mode and Ajax mode, the best solution is to define it like a regular form, but to provide, in addition to the normal submit button, a second button (<input type="button" />) to submit the form in Ajax. Symfony calls this button submit_to_remote(). This will help you build Ajax interactions that degrade gracefully. See an example in Listing 11-12.

Listing 11-12 - A Form with Regular and Ajax Submission

<div id="item_list"></div>
<?php echo form_tag('@item_add_regular') ?>
  <label for="item">Item:</label>
  <?php echo input_tag('item') ?>
  <?php if_javascript(); ?>
    <?php echo submit_to_remote('ajax_submit', 'Add in Ajax', array(
        'update'   => 'item_list',
        'url'      => '@item_add',
    )) ?>
  <?php end_if_javascript(); ?>
  <noscript>
    <?php echo submit_tag('Add') ?>
  </noscript>
</form>

Another example of combined use of remote and regular submit tags is a form that edits an article. It can offer a preview button in Ajax and a publish button that does a regular submission.

Note When the user presses the Enter key, the form is submitted using the action defined in the main <form> tag — in this example, a regular action.

Modern forms can also react not only when submitted, but also when the value of a field is being updated by a user. In symfony, you use the observe_field() helper for that. Listing 11-13 shows an example of using this helper to build a suggestion feature: Each character typed in an item field triggers an Ajax call refreshing the item_suggestion element in the page.

Listing 11-13 - Calling a Remote Function When a Field Value Changes with observe_field()

<?php echo form_tag('@item_add_regular') ?>
  <label for="item">Item:</label>
  <?php echo input_tag('item') ?>
  <div id="item_suggestion"></div>
  <?php echo observe_field('item', array(
      'update'   => 'item_suggestion',
      'url'      => '@item_being_typed',
  )) ?>
  <?php echo submit_tag('Add') ?>
</form>

The module/action written in the @item_being_typed rule will be called each time the user changes the value of the observed field (item), even without submitting the form. The action will be able to get the current item value from the value request parameter. If you want to pass something other than the value of the observed field, you can specify it as a JavaScript expression in the with parameter. For instance, if you want the action to get a param parameter, write the observe_field() helper as shown in Listing 11-14.

Listing 11-14 - Passing Your Own Parameters to the Remote Action with the with Option

<?php echo observe_field('item', array(
    'update'   => 'item_suggestion',
    'url'      => '@item_being_typed',
    'with'     => "'param=' + value",
)) ?>

Note that this helper doesn't output an HTML element, but instead outputs a behavior for the element passed as a parameter. You will see more examples of JavaScript helpers assigning behaviors later in this chapter.

If you want to observe all the fields of a form, you should use the observe_form() helper, which calls a remote function each time one of the form fields is modified.

11.3.3. Periodically Calling Remote Functions

Last but not least, the periodically_call_remote() helper is an Ajax interaction triggered every few seconds. It is not attached to an HTML control, but runs transparently in the background, as a behavior of the whole page. This can be of great use to track the position of the mouse, autosave the content of a large text area, and so on. Listing 11-15 shows an example of using this helper.

Listing 11-15 - Periodically Calling a Remote Function with periodically_call_remote()

<div id="notification"></div>
<?php echo periodically_call_remote(array(
    'frequency' => 60,
    'update'    => 'notification',
    'url'       => '@watch',
    'with'      => "'param=' + \$F('mycontent')",
)) ?>

If you don't specify the number of seconds (frequency) to wait between two calls to the remote function, the default value of 10 seconds is used. Note that the with parameter is evaluated in JavaScript, so you can use Prototype functions in it, such as the $F() function.