Hide
Search Results

Data in Mojito

This chapter will first discuss how your applications can get data and then look at how mojits can share that data with binders, other mojits, as well as how to re-hydrate data.

Sharing Data

Overview

After a mojit gets data, it may need to share that data with binders, templates, or other mojits. Mojito provides the Data addon that allows your mojit controllers and binders to share and access data. Data can also be refreshed in templates using the Data addon and Handlebars expressions.

How Is Data Shared?

Data is passed from the server to the client and vice versa using a remote procedural call (RPC) through a tunnel. During this transmission, the state of the data is preserved. When content is sent to the client as part of the page, the data and templates are rendered on the server and sent to the client through the tunnel. After the initial rendering, each time the mojit instance invokes an action that triggers an RPC through the tunnel, data is serialized and sent to the server, where the instance is recreated with the data. If the action changes the data model, the new data is then sent back through the tunnel to the client to update the data model.

Benefits

The data sharing model used in Mojito is extremely flexible, allowing you to share data in your application in many ways. We’ll look at how data is shared from the perspective of controllers and binders, which have access to the Data addon.

Controllers

Controllers can share data in the following ways:

  • rehydrate data of its template or all the templates on the page
  • share data with its binders or all the other mojit binders on the page
Binders

Binders can do the following to share/access data:

  • invoke an action to update data for another binder that is listening for changes in that data to update the view
  • rehydrate data in templates
  • share data with other binders on the page

Potential Issues

When using the Data addon to share data, you should be aware of a couple of potential issues. When multiple RPCs are made (e.g., multiple clicks on the same button), there is no guarantee of the order of execution of the requests, which means you might get stale data. Also, if your data model contains a lot of information, the payload of the RPC will negatively affect performance and security as data is transmitted back and forth between the client and server.

Data Addon

The Data addon is available through the ActionContext and mojitProxy objects from the controller and binders respectively. Because the addon has two different functions, allowing data to be shared from the server to the client and allowing one mojit to share data with other mojits, the addon has the two objects data and pageData.

Both data and pageData are fully functional Y.Model instances on the client, which enables listening for data changes and refreshing data on the page.

Data Addon Objects
Object Available Methods Scope Description
pageData get, set Page-Level Allows you to share data with other mojits on a page.
data get, set Mojit-Level Allows a controller to share data with its binders and templates.

Requiring Data Addon

The Data addon is required liked other addons in the controller by specifying the addon as a string in the required array:

}, '0.0.1', {requires: ['mojito-data-addon']});

Note

You don’t need to require the addon in binders.

Mojits Sharing Data With Its Binders and Templates

As you saw in the Data Addon Objects table, the scope of the data object is limited to a mojit. In other words, the controller can use the data object to share data with its binder or templates, but not with other mojits. When you set data with ac.data.set(key, value), the data is merged with the data passed to ac.done (this is a shallow merge). The data is also serialized and rehydrated on the client when the page is rendered in the browser.

From the controller, you use ac.data.set to set or expose data that the binder can access with the mojitProxy object. The mojitProxy accesses the set data with the data object as well with mojitProxy.data.get. Templates can have Handlebars expressions to inject the set data into a page.

The following example shows you how you would set data and then access it from the binder or the template.

Example

The example below shows how a mojit controller can share stock price information with its binder code and templates. This example shows how to access the shared data with both Handlebars expressions and using mojitProxy.pageData in the binder. In reality, you would only need to use one of these methods, with the former (Handlebars expressions) being the preferred way.

mojits/StockQuotes/controller.server.js
YUI.add('StockQuotes', function(Y, NAME) {
  Y.namespace('mojito.controllers')[NAME] = {
    index: function(ac) {
      // Model gets stock quote prices
      ac.models.get('StockQuotesModel').getData(function(err, data) {
        if (err) {
          ac.error(err);
          return;
        }
        // The data object allows the controller to set/expose the
        // variable stock_quotes that the binder and templates can access.
        ac.data.set('stock_quotes', data);
        ac.done({
          title: "Stock Quotes"
        });
      });
    }
  };
}, '0.0.1', {requires: ['mojito', 'mojito-models-addon', 'StockQuotesModel', 'mojito-data-addon']});
mojits/StockQuotes/binders/binder.js
YUI.add('StockQuotesBinderIndex', function(Y, NAME) {
  Y.namespace('mojito.binders')[NAME] = {
    init: function(mojitProxy) {
      this.mojitProxy = mojitProxy;
    },
    bind: function(node) {
      // From the mojitProxy, you use the data object to get the
      // value for stock_quotes that was set in the controller.
      var me = this,
          stock_quotes = this.mojitProxy.data.get('stock_quotes');
      this.node = node;
      var list = "<ul>";
      for (var s in stock_list) {
        list += "<li>" + s + ": $" + stock_list[s] + "</li>";
      }
      list += "</ul>";
      node.one('#stocks p').setHTML(list);
    }
  };
}, '0.0.1', {requires: ['event-mouseenter', 'mojito-client']});
mojits/StockQuotes/views/index.hb.html
<div id="{{mojit_view_id}}">
  <h2>{{title}}</h2>
  <ul>
  <!-- The Handlebars block helper can iterate through the data
       made available through ac.data.set in the controller.
  -->
  {{#each stock_quotes}}
    <li>{{.}}</li>
   {{/each}}
  </ul>
  <!-- Binder attaches the stock prices to the div container -->
  <div id="stocks">
    <p></p>
  </div>
</div>

Sharing Page Data

Page data simply means data that is scoped to the mojits within a page. The Data addon provides the pageData object, based on the YUI Model API, which has a set method for setting or exposing data that other mojits on the page can access through the get method.

The pageData object is unique to each request, but is the one store for all mojits of the request, allowing it to share data between mojits in a page. Binders can access page data with mojitProxy.pageData.get(name). Templates can use Handlebars to access page data as well, so the page data set with ac.pageData.set('name', 'foo') from one mojit can be added the the template of another mojit with {{name}}.

page Object

The page object contains all of the page data set by controllers. Templates can use {{page}} to access all of the available page data. The page object is built on the server and then sent to the client, so the page data can be shared and also re-hydrate the data on the page.

The pageData object serves as a mechanism for all mojits to access data in the page object from the client or server. Both ac.pageData and mojitProxy.pageData provide access to the same page model.

Now that you have a better understand of the page model and the page object, you can understand why passing a page object to ac.done is in your controller is not a good idea: ac.done({ page: "some data"}) will override all of the page data (the data set with pageData.set and contained in the page object). Also, data passed to ac.done or set through data or pageData object is wrapped by Mojito in``this.page``. For example, the data passed to the template with either ac.done({ stock_list: ["YHOO", "GOOG", "CSCO"]}) or ac.pagedata.set('stock_list', ["YHOO", "GOOG", "CSCO"]) can be accessed in the template with {{stock_list}} or {{this.page.stock_list}}.

Example

In this example, we’re expanding on the idea of sharing stock price information. The StockQuoteMojit mojit shares the stock price quotes with other mojits with the pageData.

As with previous example, we show how to access and attach shared data to the page with the binder and the template, but in your applications, you would normally only use one method, and the template approach is preferred.

mojits/StockQuotes/controller.server.js
YUI.add('StockQuotes', function(Y, NAME) {
  Y.namespace('mojito.controllers')[NAME] = {
    index: function(ac) {
      // Model gets stock quote prices
      ac.models.get('StockQuotesModel').getData(function(err, data) {
        if (err) {
          ac.error(err);
          return;
        }
        // The data object allows the controller to set/expose the
        // variable stock_quotes other mojits on the page can access.
        ac.pageData.set('stock_quotes', data);
        ac.done({
          title: "Stock Quotes"
        });
      });
    }
  };
}, '0.0.1', {requires: ['mojito', 'mojito-models-addon', 'StockQuotesModel', 'mojito-data-addon']});
mojits/StockQuotes/binders/index.js
YUI.add('StockQuotesBinderIndex', function(Y, NAME) {
  Y.namespace('mojito.binders')[NAME] = {
    init: function(mojitProxy) {
      this.mojitProxy = mojitProxy;
    },
    bind: function(node) {
      var ticker = null;
      // From the mojitProxy, you use the data object to get the
      // value for stock_quotes that was set in the controller.
      this.mojitProxy.pageData.on('change', function(e) {
         var ticker = e.changed;
      }
      this.node = node;
      var list = "<ul>";
      for (var s in stock_list) {
        list += "<li>" + s + ": $" + stock_list[s] + "</li>";
      }
      list += "</ul>";
      node.one('#stocks p').setHTML(list);
    }
  };
}, '0.0.1', {requires: ['event-mouseenter', 'mojito-client']});
mojits/StockTicker/binders/binder.js

In this binder, we are using an event handler to listen for updates to data. To listen to changes to any data set, you can use mojitProxy.data.on('change', doSomething). This example listens for changes to ticker_list.

YUI.add('StockTickerBinderIndex', function(Y, NAME) {

  Y.namespace('mojito.binders')[NAME] = {
    init: function(mojitProxy) {
      this.mojitProxy = mojitProxy;
    },
    bind: function(node) {
       // Listen for updates
      this.mojitProxy.pageData.on('ticker_listChange', function(e){
        var ul = node.one("#ticker"),
            items = e.newVal;
        for (var i in items) {
          ul.append("<li>" + items[i] + "</li>");
        }
      });
    }
  };
}, '0.0.1', {requires: ['event-mouseenter', 'mojito-client']});
mojits/Ticker/views/index.hb.html
<div id="{{mojit_view_id}}">
  <!-- Here we iterate through the data
       made available through ac.pageData.set in the controller of
       another mojit.
       Note: `stock_quotes` is wrapped in `this.page`, Thus
       you could use `{{#each this.page.stock_quotes}}`, too.
  -->
    {{#each stock_quotes}}
      <a href="http://finance.yahoo.com/q?s={{.}}">{{.}}</a> |&nbsp;
     {{/each}}
</div>