Take the 2-minute tour ×

My AngularJS CRUD application processes it's information over a WebSocket Server. (This was mainly so that updates from one user would get automatically pushed to all users without the need for massive HTTP polling)

I realized early on that I would have to set up my services differently than I normally do with HTTP services. Normally, for each Model that I am working with, I give them their own service to populate that particular Model. However, this is not feasible with a Websocket Connection, because I don't want a separate connection for each service. Therefore, there are a couple of solutions.

1) set up a single service that establishes a connection, then share that connection with other services that will use that service to make their specific queries

2) make a single, type-agnostic service that will be used by all controllers that need access to the connection and data.

Option 2 seemed much easier to manage and would be reusable across applications, so I started on that. That was when I realized that this was actually an opportunity. Rather than explicitly creating models for each type of data that the Client could receive, I could create a master data object, and dynamically create child objects of myService.data as needed when data flows in from requests. Thus, if I ever need to update my Model, I just update the Model at the server level, and the client already knows how to receive it; it will just need a Controller that knows how to use it.

However, this opportunity brings a drawback. Apparently, because myService.Data is an empty, childless object at creation, any Scope that wants to reference its future children have to simple reference the object itself.

For example, $scope.user = myService.data.user throws an error, because that object doesn't exist at declaration. it would appear that my only option is for each controller to simply have $scope.data = myservice.data, and the view for each controller will simply have to use < ng-model='data'>, with the declarations being something like {{data.user.username}}. I have tested it, and this does work.

My question is this; Is there any way I can get the best of both worlds? Can I have my service update it's data model dynamically, yet still have my controllers access only the part that they need? I? I was feeling quite clever until I realized that all of my Controllers were going to have access to the entire data model... But I honestly can't decide if that is even a huge problem.

Here is my Service:

app.factory('WebSocketService', ['$rootScope', function ($rootScope) {
    var factory = {
        socket: null,
        data: {},
        startConnection: function () {
            //initialize Websocket
            socket = new WebSocket('ws://localhost:2012/')
            socket.onopen = function () {
              //todo: Does anything need to happen OnOpen?
            socket.onclose = function () {
              //todo: Does anything need to happen OnClose?
            socket.onmessage = function (event) {
                var packet = JSON.parse(event.data);
                ////Model of Packet:
                ////packet.Data: A serialised Object that contains the needed data
                ////packet.Operation: What to do with the Data
                ////packet.Model: which child object of Factory.data to use
                ////packet.Property: used by Update and Delete to find a specific object with a property who's name matches this string, and who's value matches Packet.data
                //Deserialize Data
                packet.Data = JSON.parse(packet.Data);
                //"Refresh" is used to completely reload the array 
                // of objects being stored in factory.data[packet.Model]
                // Used for GetAll commands and manual user refreshes
                if (packet.Operation == "Refresh") {
                    factory.data[packet.Model] = packet.Data
                //Push is used to Add an object to an existing array of objects.  
                //The server will send this after somebody sends a successful POST command to the WebSocket Server
                if (packet.Operation == "Push") {
                if (packet.Operation == "Splice") {
                    for (var i = 0; i < factory.data[packet.Model].length; i++) {
                        for (var j = 0; j < packet.Data.length; j++){
                            if (factory.data[packet.Model][i][packet.Property] == packet.Data[j][packet.Property]) {
                                factory.data[packet.Model].splice(i, 1);
                // Used to update existing objects within the Array.  Packet.Data will be an array, although in most cases it will usually only have one value.  
                if (packet.Operation == "Update") {
                                factory.data[packet.Model][i] = packet.Data[j]
                //Sent by WebSocket Server after it has properly authenticated the user, sending the user information that it has found.  
                if (packet.Operation == "Authentication") {
                    if (packet.Data == null) {
                        //todo: Authentication Failed.  Alert User Somehow
                    else {
                        factory.data.user = packet.Data;
                        factory.data.isAuthenticated = true;

        stopConnection: function () {
            if (socket) {
        //sends a serialised command to the Websocket Server according to it's API.
        //The DataObject must be serialised as a string before it can be placed into Packet object,which will also be serialised.  
        //This is because the Backend Framework is C#, which must see what Controller and Operation to use before it knows how to properly Deserialise the DataObject.
        sendPacket: function (Controller, Operation, DataObject) {
            if (typeof Controller == "string" && typeof Operation == "string") {
                var Data = JSON.stringify(DataObject);
                var Packet = { Controller: Controller, Operation: Operation, Data: Data };
                var PacketString = JSON.stringify(Packet);
    return factory

Here is a Simple Controller that Accesses User Information. It is actually used in a permanent header <div> in the Index.html, outside of the dynamic <ng-view>. It is responsible for firing up the Websocket Connection.

App.controller("AuthenticationController", function ($scope, WebSocketService) {

    function init() {
    //this is the ONLY way that I have found to access the Service Data.
    //$scope.user = WebSocketService.data.user doesn't work
    //$scope.user = $scope.data.user doesn't even work
    $scope.data = WebSocketService.data

And here is the HTML that uses that Controller

    <div data-ng-controller="AuthenticationController">
        <span data-ng-model="data">{{data.user.userName}}</span>
share|improve this question
Promises ($q) is probably your best bet for setting up the dynamic updates. I would need to see more of you code to give you a sketch of what that would look like. –  Noah Freitas May 21 '13 at 14:56
Ooh, good point. I don't have much experience working with custom promises,so I'll update the question with some code right now. –  JHixson May 21 '13 at 14:59
Question Updated with Code and Comments. I would really appreciate your input! –  JHixson May 21 '13 at 15:32
add comment

1 Answer

up vote 1 down vote accepted

One thing you could do is store the data object on the root scope, and set up watches on your various controllers to watch for whatever controller-specific keys they need:

// The modules `run` function is called once the 
// injector is finished loading all its modules.
App.run(function($rootScope, WebSocketService) {
  $rootScope.socketData = WebSocketService.data;

// Set up a $watch in your controller
App.controller("AuthenticationController", function($scope) {
  $scope.$watch('socketData.user', function(newUser, oldUser) {
    // Assign the user when it becomes available.
    $scope.user = newUser;
share|improve this answer
I implemented this, and it works wonderfully. It makes my partials much, much cleaner now that they can have models other than "data". I'm going to wait a day and see if somebody else comes up with anything better, then mark this as the answer. –  JHixson May 21 '13 at 18:55
add comment

Your Answer


