Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to integrate websocket with emberjs?

I'm learning and building emberjs app with rails. In this app, I want the data to be pushed rather than polled to the client app.

For.e.g. the following snippet at http://awardwinningfjords.com/2011/12/27/emberjs-collections.html

// Setup a global namespace for our code.
Twitter = Em.Application.create({

  // When everything is loaded.
  ready: function() {

    // Start polling Twitter
    setInterval(function() {
      Twitter.searchResults.refresh();
    }, 2000);

    // The default search is empty, let's find some cats.
    Twitter.searchResults.set("query", "cats");

    // Call the superclass's `ready` method.
    this._super();
  }
});

It polls twitter API, but my question is how to make an EmberJS app that uses a WebSocket connection to update its state?

like image 734
Autodidact Avatar asked May 10 '12 13:05

Autodidact


2 Answers

You have to implement a DS.Adapter that understands how to handle WebSockets. Here is an simple example:

var SOCKET      = 'ws://localhost:9090/some-websocket';

var ID          = 'uuid';

var FIND        = 'find';
var FIND_MANY   = 'findMany';
var FIND_QUERY  = 'findQuery';
var FIND_ALL    = 'findAll';

/**
* Implementation of WebSocket for DS.Store
*/
App.Store = DS.Store.extend({

    revision: 4,

    adapter: DS.Adapter.create({

        socket: undefined,

        requests: undefined,

        send: function(action, type, data, result) {
            /* Specific to your web socket server side implementation */
            var request = {
                "uuid": generateUuid(),
                "action": action,
                "type": type.toString().substr(1),
                "data": data
            };
            this.socket.send(JSON.stringify(request));
            /* So I have access to the original request upon a response from the server */
            this.get('requests')[request.uuid] = request;
            return request;
        },

        find: function (store, type, id) {
            this.send(FIND, type, id);
        },

        findMany: function (store, type, ids, query) {
            this.send(FIND_MANY, type, ids);
        },

        findQuery: function (store, type, query, modelArray) {
            this.send(FIND_QUERY, type, query, modelArray).modelArray = modelArray;
        },

        findAll: function (store, type) {
            this.send(FIND_ALL, type);
        },

        /* Also implement:
         * createRecord & createRecords
         * updateRecord & updateRecords
         * deleteRecord & deleteRecords
         * commit & rollback
         */

        init: function () {

            var context = this;

            this.set('requests', {});

            var ws = new WebSocket(SOCKET);

            ws.onopen = function () {

            };

            ws.onmessage = function(event) {
                var response = JSON.parse(event.data);
                var request = context.get('requests')[response.uuid];

                switch (request.action) {
                    case FIND:
                        App.store.load(type, response.data[0]);
                        break;
                    case FIND_MANY:
                        App.store.loadMany(type, response.data);
                        break;
                    case FIND_QUERY:
                        request.modelArray.load(response.data);
                        break;
                    case FIND_ALL:
                        App.store.loadMany(type, response.data);
                        break;
                    default:
                        throw('Unknown Request: ' + request.action);
                }

                /* Cleanup */
                context.get('requests')[response.uuid] = undefined;
            };

            ws.onclose = function () {

            };

            this.set('socket', ws);
        }

    });
});
like image 177
mike Avatar answered Sep 19 '22 20:09

mike


I actually was playing around with the code from that article a few days ago. Keep the handle bar template the same, and use the following code. Obviously, this all depends on what JSON you're passing through the socket. The following code is tried and tested with ntwitter for node.

Twitter = Em.Application.create({
    ready: function() {
        var socket = io.connect();
        socket.on('message', function(json) {
            Twitter.searchResults.addTweet(Twitter.Tweet.create(JSON.parse(json)));
        });

        this._super();
    }
});

//Model
Twitter.Tweet = Em.Object.extend();

//Collection
Twitter.searchResults = Em.ArrayController.create({
    content:    [],
    _idCache:   {},

    addTweet: function(tweet) {
        var id = tweet.get("id");
        if (typeof this._idCache[id] === "undefined") {
            this.pushObject(tweet);
            this._idCache[id] = tweet.id;
        }
    }

});
like image 22
Scotty Avatar answered Sep 23 '22 20:09

Scotty