Kann AngularJS eine Ansicht automatisch aktualisieren, wenn ein beständiges Modell (Serverdatenbank) von einer externen App geändert wird?

81

Ich fange gerade erst an, mich mit AngularJS vertraut zu machen, möchte aber eine Web-App erstellen, deren Ansicht für den Benutzer in Echtzeit (ohne Aktualisierung) automatisch aktualisiert wird, wenn sich etwas in der serverseitigen Datenbank ändert.

Kann AngularJS dies (meistens) automatisch für mich erledigen? Und wenn ja, was ist der grundlegende Mechanismus bei der Arbeit?

Richten Sie AngularJS beispielsweise so ein, dass die Datenbank regelmäßig nach "Modell" -Änderungen abgefragt wird? Oder verwenden Sie einen kometenähnlichen Mechanismus, um den clientseitigen AngularJS-Code zu benachrichtigen, dass sich das Modell geändert hat?

In meiner Anwendung besteht die Herausforderung darin, dass andere (nicht webbasierte) serverseitige Software die Datenbank zeitweise aktualisiert. Diese Frage gilt jedoch auch für reine Web-Apps, bei denen möglicherweise mehrere Clients die Datenbank über AngularJS-Webclients ändern. Sie müssen jeweils aktualisiert werden, wenn einer von ihnen eine Änderung an der Datenbank (Modell) vornimmt.

jpeskin
quelle
Ich möchte hinzufügen, dass ich inzwischen herausgefunden habe, dass Meteor all dies für Sie im Framework erledigt. Das ist also meine bevorzugte Lösung für den Moment. Könnte Angular in Zukunft noch einmal ausprobieren.
Jpeskin
Meteor ist vielleicht noch zu "frisch" - es ist gut herumzuspielen, hat sich aber in der großen Produktion nicht bewährt (sicher / Skalierbarkeit / Leistung / usw.). Die Authentifizierung wurde vor etwas mehr als einem Monat hinzugefügt. Sieht gut aus, wird aber warten.
Alex Okrushko
@jpeskin Hallo. Ich bin genau dort, wo Sie waren, als Sie diese Frage gestellt haben. Was hast du am Ende gemacht? (Ich möchte Angular verwenden). Grüße Mark
mark1234

Antworten:

97

Sie haben ein paar Möglichkeiten ...

  1. Sie können alle X Millisekunden mit $timeoutund abfragen $http, oder wenn die von Ihnen verwendeten Daten mit einem REST-Service verbunden sind, können Sie $resourcestattdessen verwenden $http.

  2. Sie können einen Dienst erstellen, der eine Websocket-Implementierung verwendet scope.$applyund Änderungen verarbeitet, die vom Socket übertragen werden. Hier ist ein Beispiel mit socket.io, einer Websocket-Bibliothek von 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. Sie könnten wirklich Hightech erhalten und eine Websocket-Implementierung erstellen, die ein Angular-Modell mit dem Server synchronisiert. Wenn der Client etwas ändert, wird diese Änderung automatisch an den Server gesendet. Wenn sich der Server ändert, wird er an den Client gesendet.
    Hier ist ein Beispiel dafür in einer alten Version von Angular, die wiederum socket.io verwendet: https://github.com/mhevery/angular-node-socketio

EDIT : Für # 3 habe ich Firebase verwendet , um dies zu tun.

Andrew Joslin
quelle
Vielen Dank für eine so gründliche Antwort mit mehreren Optionen! Ich freue mich darauf, dies zu verstehen, wenn ich mehr über Angular
erfahre
4
github.com/mhevery/angular-node-socketio - hatte einen Rechtschreibfehler. behoben
Andrew Joslin
Vielen Dank für eine einfach zu verstehende Antwort, sehr hilfreich.
Mystrdat
Wie würden Sie die Ereignishandler weiter lösen, wenn der Controller zerstört werden muss?
RushPL
Brian Ford hat einen großartigen Ansatz, mit dem Sie das Ereignissystem und die Bereinigung von $ scope nutzen können. Und macht es im Allgemeinen wirklich sauber. github.com/btford/angular-socket-io . Schauen Sie sich socket.forward ()
Andrew Joslin
15

Hier ist eine Implementierung, die einen Steg anstelle eines Knotens verwendet. Der Angularjs-Teil basiert auf der Angular-Seed-App. Ich bin nicht sicher, ob der Winkelcode idiomatisch ist ... aber ich habe getestet, dass dies funktioniert. HTH -Todd.

TimerWebSocketServlet siehe

https://gist.github.com/3047812

controller.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
quelle
Dies ist ein brillantes Beispiel. Ich lerne gerade Angular.js und habe mich gefragt, ob Sie die komplette App mit Vorlagen usw. haben, von der Sie lernen können.
Mac
0

Laut dem Buch "Discover Meteor" ähneln Angular-Uhren / Zielfernrohren den Berechnungen von Meteor in Bezug auf die Reaktivität ... aber Angular ist nur für Kunden verfügbar und bietet eine weniger detaillierte Kontrolle als Meteor.

Mein Eindruck ist, dass die Verwendung von Angular besser geeignet ist, um einer vorhandenen App Reaktivität zu verleihen, während Meteor steigt, wenn Sie es für das Ganze verwenden. Aber ich habe noch keine wirklichen Erfahrungen mit Angular (obwohl ich einige kleine Meteor-Apps erstellt habe).

zweiwöchentlich
quelle
0

Daher hat Andy Joslin in seiner Antwort die beste Lösung in meiner Meinung erwähnt, die dritte Option, die darin besteht, den Status bidirektional über Websockets oder eine andere asynchrone Bibliothek, mit der Sie sich befassen, aufrechtzuerhalten (dies wäre die Chrome-Nachrichten-API für Chrome-Erweiterungen und Apps zum Beispiel) und toddg haben ein Beispiel dafür gegeben, wie dies erreicht werden würde. In seinem Beispiel implementiert er jedoch ein Anti-Pattern in AngularJS: Der Dienst ruft den Controller auf. Stattdessen sollte das Modell innerhalb des Dienstes platziert und dann von der Steuerung aus referenziert werden.

Die Service-Socket-Rückrufe ändern das Servicemodell und aktualisieren die Ansicht, da auf sie vom Controller verwiesen wird. Vorsicht, wenn Sie mit primitiven Datentypen oder Variablen arbeiten, die jedoch neu zugewiesen werden können, benötigen diese eine Überwachung des Controllers, damit dies funktioniert.

Bluehallu
quelle