¿Puede AngularJS actualizar automáticamente una vista si una aplicación externa cambia un modelo persistente (base de datos del servidor)?

81

Estoy empezando a familiarizarme con AngularJS, pero me gustaría crear una aplicación web que tenga una vista que se actualice automáticamente en tiempo real (sin actualización) para el usuario cuando algo cambia en la base de datos del lado del servidor.

¿Puede AngularJS manejar esto (en su mayoría) automáticamente para mí? Y si es así, ¿cuál es el mecanismo básico en funcionamiento?

Por ejemplo, ¿de alguna manera configura AngularJS para sondear la base de datos regularmente en busca de cambios en el "modelo"? ¿O usar algún tipo de mecanismo similar a Comet para notificar al código del lado del cliente de AngularJS que el modelo ha cambiado?

En mi aplicación, el desafío es que otro software del lado del servidor (no web) actualizará la base de datos en ocasiones. Pero esta pregunta se aplica igualmente a las aplicaciones web puras en las que es posible que varios clientes cambien la base de datos a través de los clientes web de AngularJS, y cada uno de ellos debe actualizarse cuando uno de ellos realiza un cambio en la base de datos (modelo).

jpeskin
fuente
Me gustaría agregar que desde entonces descubrí que Meteor hace todo esto por usted en el marco, así que esa es mi solución preferida por ahora. Podría comprobar Angular de nuevo en el futuro.
jpeskin
Es posible que Meteor sea todavía demasiado "nuevo"; es bueno jugar, pero no ha demostrado su eficacia en una gran producción (seguridad / escalabilidad / rendimiento / etc.) La autenticación se agregó hace poco más de un mes. Se ve bien, pero esperará.
Alex Okrushko
@jpeskin Hola. Estoy exactamente donde estabas cuando hiciste esta pregunta. lo acabaste haciendo? (Me gustaría usar Angular). Saludos Mark
mark1234

Respuestas:

97

Tienes algunas opciones ...

  1. Puede hacer un sondeo cada X milisegundos usando $timeouty $http, o si los datos que está usando están conectados a un servicio REST, podría usar en $resourcelugar de $http.

  2. Puede crear un servicio que use alguna implementación de Websocket y se use scope.$applypara manejar los cambios que empuja el socket. Aquí hay un ejemplo usando socket.io, una biblioteca websocket de node.js:

    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. Podría obtener realmente alta tecnología y crear una implementación de websocket que sincronice un modelo angular con el servidor. Cuando el cliente cambia algo, ese cambio se envía automáticamente al servidor. O si el servidor cambia, se envía al cliente.
    Aquí hay un ejemplo de eso en una versión anterior de Angular, nuevamente usando socket.io: https://github.com/mhevery/angular-node-socketio

EDITAR : Para el n. ° 3, he estado usando Firebase para hacer esto.

Andrew Joslin
fuente
¡Gracias por una respuesta tan completa con varias opciones! Espero
darle
4
github.com/mhevery/angular-node-socketio : tuvo un error de ortografía. lo arregló
Andrew Joslin
Gracias por una respuesta fácil de entender, muy útil.
mystrdat
¿Cómo continuaría desvinculando los controladores de eventos si el controlador necesita ser destruido?
RushPL
Brian ford tiene un gran enfoque que le permite aprovechar el sistema de eventos y la limpieza de $ scope. Y lo hace realmente limpio en general. github.com/btford/angular-socket-io . Mire socket.forward ()
Andrew Joslin
15

Aquí hay una implementación que usa jetty en lugar de node. La parte angularjs se basa en la aplicación angular-seed. No estoy seguro de si el código angular es idiomático ... pero he probado que funciona. HTH -Todd.

TimerWebSocketServlet ver

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
fuente
Este es un ejemplo brillante. Estoy aprendiendo Angular.js, y me preguntaba si tiene la aplicación completa con plantillas, etc., para aprender.
mac
0

Según el libro "Discover Meteor", los relojes / alcances de Angular son similares a los cálculos de Meteor con respecto a la reactividad ... pero Angular es solo para el cliente y brinda un control menos granular que Meteor.

Mi impresión es que usar Angular podría ser una mejor opción para agregar reactividad a una aplicación existente, mientras que Meteor se dispara cuando lo usa para todo. Pero todavía no tengo experiencia real con Angular (aunque he creado algunas aplicaciones pequeñas de Meteor).

semanalmente
fuente
0

Entonces, Andy Joslin ha mencionado la mejor solución en mi opinión en su respuesta, la tercera opción, que es mantener el estado bidireccionalmente a través de websockets o cualquier otra biblioteca asíncrona con la que esté tratando (esta sería la API de mensajes de Chrome para extensiones de Chrome y Apps, por ejemplo), y toddg ha dado un ejemplo de cómo se lograría. Sin embargo, en su ejemplo está implementando un anti-patrón en AngularJS: el servicio está llamando al controlador. En cambio, el modelo debe colocarse dentro del servicio y luego referenciarse desde el controlador.

Las devoluciones de llamada de socket de servicio modificarán el modelo de servicio y, como se hace referencia a él desde el controlador, actualizará la vista. Sin embargo, tenga cuidado si está tratando con tipos de datos primitivos o variables que se pueden reasignar, ya que necesitarán un control en el controlador para que esto funcione.

bluehallu
fuente