81

I'm just starting to familiarize with AngularJS, but I would like to build a web app that has a view that gets auto-upated in real-time (no refresh) for the user when something changes in the server-side database.

Can AngularJS handle this (mostly) automatically for me? And if so, what is the basic mechanism at work?

For example, do you somehow setup AngularJS to poll the DB regularly for "model" changes? Or use some sort of Comet-like mechanism to notify AngularJS client-side code that the model has changed?

In my application, the challenge is that other (non-web) server-side software will be updating the database at times. But this question applies equally to pure web-apps where you might have multiple clients changing the database through AngularJS web clients, and they each need to be updated when one of them makes a change to the DB (model).

georgeawg
  • 48,608
  • 13
  • 72
  • 95
jpeskin
  • 2,801
  • 4
  • 28
  • 27
  • I'd like to add that I've since discovered that Meteor does all this for you in the framework, so that's my preferred solution for now. Might check out Angular again in the future. – jpeskin Nov 15 '12 at 06:40
  • Meteor might be still too "fresh" - it's good to play around, but has not proved itself in big production (safely/scalability/performance/etc). Authentication was added just over the month ago. Looks good, but will wait. – Alex Okrushko Dec 13 '12 at 15:15
  • @jpeskin Hi. I'm just about exactly where you were when you asked this question. What did you end up doing? (I would like to use Angular). Regards Mark – mark1234 Jun 16 '14 at 20:45
  • Check [Making Angular.js realtime with Websockets (with Pusher)](http://blog.pusher.com/making-angular-js-realtime-with-pusher/) [Build a real-time polls application with Node.js, Express, AngularJS, and MongoDB](http://www.ibm.com/developerworks/library/wa-nodejs-polling-app/). – TMichel Jun 21 '14 at 18:52

5 Answers5

97

You have a few choices...

  1. You could do polling every X milliseconds using $timeout and $http, or if the data you're using is hooked up to a REST service, you could use $resource instead of $http.

  2. You could create a service that uses some Websocket implementation and uses scope.$apply to handle changes that are pushed by the socket. Here's an example using socket.io, a node.js websocket library:

    myApp.factory('Socket', function($rootScope) {
        var socket = io.connect('http://localhost:3000');
    
        //Override socket.on to $apply the changes to angular
        return {
            on: function(eventName, fn) {
                socket.on(eventName, function(data) {
                    $rootScope.$apply(function() {
                        fn(data);
                    });
                });
            },
            emit: socket.emit
        };
    })
    
    function MyCtrl($scope, Socket) {
        Socket.on('content:changed', function(data) {
            $scope.data = data;
        });
        $scope.submitContent = function() {
            socket.emit('content:changed', $scope.data);
        };
    }
    
  3. You could get really high tech and create a websocket implementation which syncs an Angular model with the server. When the client changes something, that change gets automatically sent to the server. Or if the server changes, it gets sent to the client.
    Here's an example of that in an old version of Angular, again using socket.io: https://github.com/mhevery/angular-node-socketio

EDIT: For #3, I've been using Firebase to do this.

Andrew Joslin
  • 43,033
  • 21
  • 100
  • 75
  • Thanks for such a thorough reply with several options! Looking forward to making sense of this as I learn more about Angular :) – jpeskin Jul 01 '12 at 00:23
  • 4
    https://github.com/mhevery/angular-node-socketio - had a speling mistake. fixed it – Andrew Joslin Jul 10 '12 at 13:32
  • Thank you for a simple to understand reply, very helpful. – zrooda Jul 17 '12 at 12:52
  • How would you go on unbinding the event handlers if the controller needs to be destroyed? – RushPL Nov 04 '13 at 14:14
  • Brian ford has a great approach that allows you to piggyback on the $scope's event system & cleanup. And makes it really clean in general. https://github.com/btford/angular-socket-io. Look at socket.forward() – Andrew Joslin Nov 05 '13 at 20:44
  • @AndrewJoslin would be great to see an example of the first answer, id like to check if any data has changed before even completing request. Is this possible? – alphapilgrim Feb 10 '16 at 19:59
15

Here's an implementation that uses jetty instead node. The angularjs part is based on the angular-seed app. I'm not sure if the angular code is idiomatic...but I've tested that this works. HTH -Todd.

TimerWebSocketServlet see

https://gist.github.com/3047812

controllers.js

// -------------------------------------------------------------
// TimerCtrl
// -------------------------------------------------------------
function TimerCtrl($scope, CurrentTime) {
    $scope.CurrentTime = CurrentTime;
    $scope.CurrentTime.setOnMessageCB(
        function (m) {
            console.log("message invoked in CurrentTimeCB: " + m);
            console.log(m);
            $scope.$apply(function(){
                $scope.currentTime = m.data;
            })
        });
}
TimerCtrl.$inject = ['$scope', 'CurrentTime'];

services.js

angular.module('TimerService', [], function ($provide) {
    $provide.factory('CurrentTime', function () {
        var onOpenCB, onCloseCB, onMessageCB;
        var location = "ws://localhost:8888/api/timer"
        var ws = new WebSocket(location);
        ws.onopen = function () {
            if(onOpenCB !== undefined)
            {
                onOpenCB();
            }
        };
        ws.onclose = function () {
            if(onCloseCB !== undefined)
            {
                onCloseCB();
            }
        };
        ws.onmessage = function (m) {
            console.log(m);
            onMessageCB(m);
        };

        return{
            setOnOpenCB: function(cb){
               onOpenCB = cb;
            },
            setOnCloseCB: function(cb){
                onCloseCB = cb;
            },
            setOnMessageCB: function(cb){
                onMessageCB = cb;
            }
        };
    })});

web.xml

<servlet>
    <servlet-name>TimerServlet</servlet-name>
    <servlet-class>TimerWebSocketServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>TimerServlet</servlet-name>
    <url-pattern>/api/timer/*</url-pattern>
</servlet-mapping>
toddg
  • 351
  • 2
  • 5
  • This is a brilliant example. I am just learning Angular.js, and was wondering if you have the complete app with templates etc, to learn from? – mac Oct 12 '12 at 01:22
3

What you are looking for is Firebase and Deployd. Firebase comes with an adapter too that makes using it a breeze: http://angularfire.com/

Kenneth Lynne
  • 15,461
  • 12
  • 63
  • 79
0

According to the "Discover Meteor" book, Angular watches/scopes are similar to Meteor's computations regarding reactivity... but Angular is client-only and gives less-granular control than Meteor.

My impression is that using Angular might be a better fit for adding reactivity to an existing app, whereas Meteor soars when you use it for the whole thing. But I have no real experience with Angular yet (though I have built some small Meteor apps).

cweekly
  • 8,706
  • 1
  • 22
  • 17
0

So, Andy Joslin has mentioned the best solution in my opnion in his answer, the 3rd option, which is to maintain state bidirectionally via websockets or whatever other async library you're dealing with (this would be the Chrome message API for Chrome Extensions and Apps for instance), and toddg has given an example of how that would be achieved. However, in his example he is implementing an anti-pattern in AngularJS: the service is calling the controller. Instead, the model should be placed inside the service, and then referenced from the controller.

The service socket callbacks will modify the service model, and because it is referenced from the controller, it will update the view. Careful if you're dealing with primitive data types or variables that can be reassigned though, those will need a watch on the controller to make this work.

bluehallu
  • 10,205
  • 9
  • 44
  • 61