Passing complex data from Smarty to JavaScript

Joor Loohuis, November 13, 2009, 21855 views.

Modern web applications frequently need to process complex data with JavaScript. Typically, these data are requested separately through AJAX calls, but if the data do not change frequently, it is probably easier to output JSON in the (X)HTML, and let JavaScript take it from there. The Smarty template engine makes this more or less trivial.

Tags: , , , , , ,

It is very common for a web application to process complex data, and generate output on the fly. An example would be a Google Map that is supposed to display a set of locations, probably with associated information windows that appear when a marker is clicked. The most common and flexible way of processing the data, would be to have them stored in a data structure in JavaScript. The problem is how to get the data into the data structure to begin with. One solution is to have the JavaScript make an AJAX call to a script that returns the data, for example in the form of JSON. Besides complicating the code, this requires additional requests to the server, resulting in a lesser performance of the entire application. If the data do not change frequently, or more specifically, not in the timespan it takes a visitor to view the data, it is possible to send the data together with the (X)HTML interface, and let JavaScript start processing it the moment the document has loaded. This is very easy to do in a Smarty template.

Emitting data

Suppose you have a complex data set, for example an array of objects with information you want to process through JavaScript. The simplest way of getting the information into a JavaScript data structure is to pass it in the form of JSON. This means that the template that contains the interface needs to generate a JSON representation from the original data structure. Since Smarty can use any PHP function as a variable modifier, this task is more or less trivial. We use the PHP function json_encode as a modifier on our data structure:

<form>
  <input type="hidden" id="jsondata" value='{$mydata|@json_encode}'>
</form>

We're using a small invisible form to embed a hidden input, which we can acces through its id. The value is the JSON encoded string of the entire data structure in $mydata. We need to tell Smarty that $mydata is a complex data structure, hence the @ prepended to the modifier. The resulting data can be read into JavaScript with a simple call. For example, with Prototype we could do something like

var myData = $F('jsondata').evalJSON();

That is all that is required to have the data available in JavaScript. No AJAX required.

The catch...

There is a small catch with this approach. It is likely that the JSON string will contain some characters with a special meaning in (X)HTML, or that break the quoting of the document. To solve that, we need to roll our own little Smarty plugin, which can take care of converting offending characters. Save the PHP code below to a file named function.json_encode.php:

<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     function.json_encode.php
 * Type:     function
 * Name:     json_encode
 * Parms:    data: data source
 *           assign: variable to assign result to
 *           quote: process data with htmlentities before
 *                  generating JSON
 * Purpose:  output data in JSON format
 * License:  this code is distributed under the PHP License v3.01
 *           Copyright 2009, Loco (Loohuis Consulting)
 * -------------------------------------------------------------
 */
function smarty_function_json_encode($params, &$smarty)
{
    if ($params'quote')
        $out = json_encode(_quote($params'data'));
    else
        $out = json_encode($params'data');
    if ($params'assign')
        $smarty->assign($params'assign', $out);
    else
        return $out;
}
// apply htmlspecialchars, recurse into data structures
function _quote($data)
{
    if (is_object($data)) {
        $vars = get_object_vars($data);
    if (count($vars))
        foreach ($vars as $k => $v)
            $data->$k = _quote($v);
    }
    elseif (is_array($data)) {
        if (count($data))
            foreach ($data as $k => $v)
                $data$k = _quote($v);
    }
    elseif (is_string($data)) {
        $data = htmlspecialchars($data, ENT_QUOTES);
    }
    return $data;
}
?>

Put this file in a location that is listed in the PHP include_path. Now you can emit JSON data as follows:

<form>
  <input type="hidden" id="jsondata" value='{json_encode data=$mydata}'>
</form>

or, in case you expect to have data that will break the (X)HTML:

<form>
  <input type="hidden" id="jsondata" value='{json_encode data=$mydata quote=true}'>
</form>

In the latter case, you might need to do some processing in JavaScript if you need to get rid of the entites again. In any case, passing complex data to JavaScript doesn't need to be more difficult than this.

Social networking: Tweet this article on Twitter Pass on this article on LinkedIn Bookmark this article on Google Bookmark this article on Yahoo! Bookmark this article on Technorati Bookmark this article on Delicious Share this article on Facebook Digg this article on Digg Submit this article to Reddit Thumb this article up at StumbleUpon Submit this article to Furl

Talkback

respond to this article