Czy AngularJS może automatycznie zaktualizować widok, jeśli model trwały (baza danych serwera) zostanie zmieniony przez zewnętrzną aplikację?

81

Zaczynam dopiero zapoznawać się z AngularJS, ale chciałbym zbudować aplikację internetową, która ma widok, który jest automatycznie aktualizowany w czasie rzeczywistym (bez odświeżania) dla użytkownika, gdy coś się zmieni w bazie danych po stronie serwera.

Czy AngularJS może sobie z tym poradzić (w większości) automatycznie? A jeśli tak, to jaki jest podstawowy mechanizm w działaniu?

Na przykład, czy w jakiś sposób skonfigurowałeś AngularJS tak, aby regularnie sprawdzał bazę danych pod kątem zmian „modelu”? Albo użyć jakiegoś mechanizmu podobnego do Comet, aby powiadomić kod klienta AngularJS o zmianie modelu?

W mojej aplikacji wyzwaniem jest to, że inne (inne niż internetowe) oprogramowanie po stronie serwera będzie czasami aktualizować bazę danych. Ale to pytanie dotyczy w równym stopniu aplikacji internetowych, w których może być wielu klientów zmieniających bazę danych za pośrednictwem klientów sieciowych AngularJS i każdy z nich musi zostać zaktualizowany, gdy jeden z nich dokona zmiany w DB (modelu).

jpeskin
źródło
Chciałbym dodać, że odkąd odkryłem, że Meteor robi to wszystko za Ciebie we frameworku, więc na razie jest to moje preferowane rozwiązanie. Może w przyszłości ponownie sprawdzę Angular.
jpeskin
Meteor może być wciąż zbyt „świeży” - dobrze się bawić, ale nie sprawdził się w dużej produkcji (bezpieczeństwo / skalowalność / wydajność / itp.). Uwierzytelnianie zostało dodane nieco ponad miesiąc temu. Wygląda dobrze, ale będzie czekać.
Alex Okrushko
@jpeskin Hi. Jestem dokładnie tam, gdzie byłeś, kiedy zadałeś to pytanie. Co ostatecznie zrobiłeś? (Chciałbym użyć Angulara). Pozdrawiam Mark
mark1234

Odpowiedzi:

97

Masz kilka możliwości ...

  1. Możesz przeprowadzić odpytywanie co X milisekund przy użyciu $timeouti $http, lub jeśli dane, których używasz, są podłączone do usługi REST, możesz użyć $resourcezamiast $http.

  2. Można utworzyć usługę, która używa pewnej implementacji Websocket i używa scope.$applydo obsługi zmian wypychanych przez gniazdo. Oto przykład użycia socket.io, biblioteki websocket 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. Możesz uzyskać naprawdę zaawansowaną technologię i stworzyć implementację websocket, która zsynchronizuje model Angular z serwerem. Kiedy klient coś zmienia, ta zmiana jest automatycznie wysyłana na serwer. Lub jeśli serwer się zmieni, zostanie wysłany do klienta.
    Oto przykład tego w starej wersji Angular, ponownie przy użyciu socket.io: https://github.com/mhevery/angular-node-socketio

EDYCJA : W przypadku # 3 używam do tego Firebase .

Andrew Joslin
źródło
Dzięki za tak dokładną odpowiedź z kilkoma opcjami! Z niecierpliwością czekam, aż zrozumiem to, gdy dowiem się więcej o Angular :)
jpeskin
4
github.com/mhevery/angular-node-socketio - miał błąd w pisowni. naprawiono to
Andrew Joslin
Dziękuję za prostą do zrozumienia odpowiedź, bardzo pomocną.
mystrdat
Jak postępowałbyś w usuwaniu powiązań programów obsługi zdarzeń, jeśli kontroler musi zostać zniszczony?
RushPL
Brian ford ma świetne podejście, które pozwala ci na piggyback w systemie zdarzeń $ scope i porządkowaniu go. I ogólnie sprawia, że ​​jest naprawdę czysty. github.com/btford/angular-socket-io . Spójrz na socket.forward ()
Andrew Joslin
15

Oto implementacja, która używa molo zamiast węzła. Część angularjs jest oparta na aplikacji angular-seed. Nie jestem pewien, czy kod kątowy jest idiomatyczny ... ale przetestowałem, że to działa. HTH -Todd.

TimerWebSocketServlet patrz

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
źródło
To wspaniały przykład. Właśnie uczę się Angular.js i zastanawiam się, czy masz kompletną aplikację z szablonami itp., Z której możesz się uczyć?
Mac,
0

Zgodnie z książką „Discover Meteor”, zegarki / lunety Angular są podobne do obliczeń Meteor w zakresie reaktywności ... ale Angular jest tylko klientem i zapewnia mniej szczegółową kontrolę niż Meteor.

Mam wrażenie, że używanie Angulara może lepiej nadawać się do dodawania reaktywności do istniejącej aplikacji, podczas gdy Meteor szybuje, gdy używasz go do całości. Ale nie mam jeszcze prawdziwego doświadczenia z Angularem (chociaż zbudowałem kilka małych aplikacji Meteor).

co tydzień
źródło
0

Tak więc Andy Joslin wspomniał w swojej odpowiedzi o najlepszym rozwiązaniu z mojej opinii, trzeciej opcji, która polega na dwukierunkowym utrzymywaniu stanu za pośrednictwem gniazd sieciowych lub jakiejkolwiek innej biblioteki asynchronicznej, z którą masz do czynienia (byłby to interfejs API wiadomości Chrome dla rozszerzeń Chrome i Apps na przykład), a toddg podał przykład, jak można to osiągnąć. Jednak w swoim przykładzie implementuje anty-wzorzec w AngularJS: usługa wywołuje kontroler. Zamiast tego model należy umieścić w usłudze, a następnie odwołać się do niego z kontrolera.

Wywołania zwrotne gniazda usługi zmodyfikują model usługi, a ponieważ odwołuje się do niego kontroler, zaktualizuje widok. Ostrożnie, jeśli masz do czynienia z prymitywnymi typami danych lub zmiennymi, które można ponownie przypisać, będą one wymagały zegarka na kontrolerze, aby to zadziałało.

bluehallu
źródło