Tuesday, 29 November 2011

HTML/JSON and HTTPResponse class for AJAX responses


Ajax responses will sometimes need to update existing DOM elements, for example refresh a set of search results. Returning plain HTML is generally a good default behaviour, as it allows you to keep template rendering in one place (in SilverStripe PHP code), and is easy to deal with in JavaScript.

If you need to process or inspect returned data, consider extracting it from the loaded HTML instead (through id/class attributes, or the jQuery.metadata plugin). For returning status messages, please use the HTTP status-codes.
Only return evaluated JavaScript snippets if unavoidable. Most of the time you can just pass data around, and let the clientside react to changes appropriately without telling it directly through JavaScript in AJAX responses. Don't use the Form SilverStripe class, which is built solely around this inflexible concept.
Example: Autocomplete input field loading page matches through AJAX

Template:


    <ul>
    <% control Results %>
      <li id="Result-$ID">$Title</li>
    <% end_control %>
    </ul>




PHP:
               
class MyController {
  function autocomplete($request) {
    $SQL_title = Convert::raw2sql($request->getVar('title'));
    $results = DataObject::get("Page", "Title = '$SQL_title'");
    if(!$results) return new HTTPResponse("Not found", 404);
     
    // Use HTTPResponse to pass custom status messages
    $this->response->setStatusCode(200, "Found " . $results->Count() . " elements");
     
    // render all results with a custom template
    $vd = new ViewableData();
    return $vd->customise(array(
      "Results" => $results
    ))->renderWith('AutoComplete');
  }
}




HTML


          
<form action"#">
  <div class="autocomplete {url:'MyController/autocomplete'}">
    <input type="text" name="title" />
    <div class="results" style="display: none;">
  </div>
  <input type="submit" value="action_autocomplete" />
</form>




JavaScript:



                 

$('.autocomplete input').live('change', function() {
  var resultsEl = $(this).siblings('.results');
  resultsEl.load(
    // get form action, using the jQuery.metadata plugin
    $(this).parent().metadata().url,
    // submit all form values
    $(this.form).serialize(),
    // callback after data is loaded
    function(data, status) {
      resultsEl.show();
      // get all record IDs from the new HTML
      var ids = jQuery('.results').find('li').map(function() {
        return $(this).attr('id').replace(/Record\-/,'');
      });
    }
  );
});


Although they are the minority of cases, there are times when a simple HTML fragment isn't enough. For example, if you have server side code that needs to trigger the update of a couple of elements in the CMS left-hand tree, it would be inefficient to send back the HTML of entire tree. SilverStripe can serialize to and from JSON (see the Convert class), and jQuery deals very well with it through jQuery.getJSON(), as long as the HTTP content-type is properly set.


No comments:

Post a Comment

Visual comparison of the two methods, creating a simple table.

Option 1, using PHP: // PHP $html = '<table>' ;     foreach ( $data as $row ) {     $html .= '<tr>...