A Simple Ember.js Demo for Lightstreamer

In this post we are going to make a simple exercise to show how quickly you can integrate Lightstreamer with Ember.js, a popular JavaScript MVC framework aimed at making easier the development of so-called Single Page Applications.

Let’s start by delving into the code example.

The Application Code

As required by every Ember application, we first need to create a new instance of the Application class:

var StockListDemoApp = Ember.Application.create({
  LOG_TRANSITIONS: true,
  LOG_BINDINGS: true,
  LOG_VIEW_LOOKUPS: true,
  LOG_STACKTRACE_ON_DEPRECATION: true,
  LOG_VERSION: true,
  debugMode: true
});

Then, we define the “stock” resource, mapped to the index “/” of our application:

StockListDemoApp.Router.map(function() {
  this.resource("stock", { path : "/" } );
})

Now it is time to create a model class to describe a Stock Item, in which all attributes map to the field schema specified in the subscription request, as will be shown later on:

StockListDemoApp.StockItem = DS.Model.extend({
  stock_name: DS.attr(),
  last_price: DS.attr(),
  time: DS.attr(),
  pct_change: DS.attr(),
  bid_quantity: DS.attr(),
  bid: DS.attr(),
  ask: DS.attr(),
  ask_quantity: DS.attr(),
  min: DS.attr(),
  max: DS.attr(),
  ref_price: DS.attr(),
  open_price: DS.attr(),
});

As a simple and fast way to persist our data, we decide to lean on the local-storage adapter, a third-party Ember Adapter which stores the object instances into the browser’s local database. To do that, let’s initialize the ApplicationAdapter:

StockListDemoApp.ApplicationAdapter = DS.LSAdapter.extend({
  namespace: 'stock-list'
});

The StockRoute class is the core of the example. In the snippet below the complete implementation:

StockListDemoApp.StockRoute = Ember.Route.extend({

  activate: function() {
    store = this.store;
    require(["LightstreamerClient", "Subscription"], function(LightstreamerClient, Subscription) {
      var lsClient = new LightstreamerClient("http://push.lightstreamer.com","DEMO");
      lsClient.connectionSharing.enableSharing("DemoCommonConnection", "ATTACH", "CREATE");
      lsClient.addListener(new StatusWidget("left", "0px", true));
      lsClient.connect();

      var stockSubscription = new Subscription("MERGE", 
        ["item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"],
        ["stock_name", "last_price", "time", "pct_change", "bid_quantity", "bid", "ask", "ask_quantity", "min", "max", "ref_price", "open_price"]
      );

      stockSubscription.setDataAdapter('QUOTE_ADAPTER');
      stockSubscription.setRequestedSnapshot("yes");
      stockSubscription.addListener({
        onItemUpdate: function(info) {
          var i = info.getItemPos();
          if (!store.hasRecordForId('stockItem', i)) {
            // Push an empty record, with only the primary key
            store.push('stockItem', { id: i});
          }

          store.find('stockItem', i).then(function(stockItem) {
            info.forEachChangedField(function(fieldName, fieldPos, value) {
              // Set field value on the stockItem locally-persisted instance
              stockItem.set(fieldName, value)
            });
            // Commit the changes on the local store
            stockItem.save();
          });
	},
      });

      // Register the subscription
      lsClient.subscribe(stockSubscription);
    });
  },
	  
  model: function() {
    // The model is provided by retrieving all the stored stockItems
    return this.store.findAll('stockItem');
  },
});

In the activate hook we make the real integration between Ligthstreamer and Ember.js happen. As you can see, the onItemUpdate callback manages the update of the model that backs the template (as will be defined in the next section). Once a new item event comes from the server, a find from the store is executed to lookup the matching object, in order to update its attribute values according to the modified item fields.

Finally, in the model hook we simply return all the objects retrieved from the local storage, by invoking  the store’s findAll method. In this way, each change to an object performed in the onItempUpdate function will be automatically reflected by the framework in the HTML page.

The HTML Code

In order to display the real-time data updates flowing in from the server, we need to define a template:

<script type="text/x-handlebars" data-template-name="stock">
  <table cellspacing="0" cellpadding="2" width="780" border="0" >
    <thead>
      <tr>
        <td>Name</td>
        <td>Last</td>
        <td>Time</td>
        <td>Change</td>
        <td>Bid Size</td>
        <td>Bid</td>
        <td>Ask</td>
        <td>Ask Size</td>
        <td>Min</td>
        <td>Max</td>
        <td>Ref.</td>
        <td>Open</td>
      </tr>
    </thead>
	  
    <tbody>
      {{#each item in model}}
      <tr>
        <td>{{item.stock_name}}</td>
        <td>{{item.last_price}}</td>
        <td>{{item.time}}</td>
        <td>{{item.pct_change}}</td>
        <td>{{item.bid_quantity}}</td>
        <td>{{item.bid}}</td>
        <td>{{item.ask}}</td>
        <td>{{item.ask_quantity}}</td>
        <td>{{item.min}}</td>
        <td>{{item.max}}</td>
        <td>{{item.ref_price}}</td>
        <td>{{item.open_price}}</td>
      </tr>
      {{/each}}
    </tbody>
  </table>
</script>

In the above snippet, we iterate through the model provided by the StockRoute class defined above and, for each item, a new row table is rendered providing in each cell the corresponding attribute value.

The full code example is available on Github: https://github.com/Lightstreamer/Lightstreamer-example-StockList-client-ember

And here you can see the final live result.

August 16, 2024
Originally published: January 28, 2015


3 min read

Table of Contents