globalna komunikacja w module angular: szyna zdarzeń lub wzór/usługa mediatora

Do tej pory widziałem wiele rozwiązań tego problemu. Najprostszym jest oczywiście $emit Zdarzenie w $rootScope jako szyna zdarzeń np. ( https://github.com/btilford/anti-patterns/blob/master/angular/Angular.md )

angular.module('myModule').directive('directiveA', function($rootScope) {
  return {
    link : function($scope, $element) {
      $element.on('click', function(event) {
        $rootScope.$emit('directiveA:clicked', event);
      });
    }
  }
});
angular.module('myModule').directive('directiveB', function() {
  return {
    link : function($scope, $element) {
      $rootScope.on('directiveA:clicked', function(event) {
        console.log('received click event from directiveA');
      });
    }
  }
});

A innym jest zadeklarowanie usługi z mediatorem lub funkcjonalnością pubsub / zamkniętym zakresem np. (komunikowanie się między wieloma kontrolerami i dyrektywą . )

module.factory('MessageService',
  function() {
    var MessageService = {};

    var listeners = {};
    var count = 0;
    MessageService.registerListener = function(listener) {
      listeners[count] = listener;
      count++;

      return (function(currentCount) {
        return function() {
          delete listeners[currentCount];
        }
      })(count);
    }

    MessageService.broadcastMessage = function(message) {
      var keys = Object.keys(listeners);

      for (var i = 0; i < keys.length; i++) {
        listeners[keys[i]](message);
      }
    }

    return MessageService;
  }
);

Pytanie brzmi:

  • czy jest sens aby użyć drugiego w aplikacji kątowej?
  • a jakie są plusy i minusy każdego z nich w porównaniu do siebie?
Author: Community, 2014-12-08

3 answers

Tworzenie własnej implementacji emitera zdarzeń jest nieproduktywne przy pisaniu aplikacji AngularJS. Angular dostarcza już wszystkie narzędzia potrzebne do komunikacji opartej na zdarzeniach.

  • używanie $emit Na $rootScope dobrze sprawdza się w globalnej komunikacji międzyserwisowej i tak naprawdę nie ma żadnych wad.
  • użycie $broadcast na naturalnym zakresie (takim, który jest związany z częścią DOM) zapewnia komunikację między komponentami widoku (dyrektywami, Kontrolery).
  • użycie $broadcast na $rootScope łączy dwa poprzednie punkty (zapewnia całkowicie globalną platformę komunikacyjną). jest to rozwiązanie używane w zasadzie przez każdą bibliotekę opartą na AngularJS.

I

  • Jeśli martwisz się o wydajność poprzedniej opcji i naprawdę chcesz mieć oddzielny emiter zdarzeń, możesz łatwo go utworzyć, tworząc izolowany zakres ($rootScope.$new(true)) i używając $broadcast na nim. (Można go następnie owinąć w usługę i wstrzyknąć go w dowolne miejsce.)

Ostatnia opcja tworzy pełnoprawny emiter zdarzeń zintegrowany z Angular (implementacja podana w twoim pytaniu musiałaby przynajmniej zawinąć wszystkie wywołania słuchacza w $apply(), aby poprawnie zintegrować) , które mogą być dodatkowo wykorzystane do obserwacji zmian danych, jeśli pasuje do konkretnego przypadku użycia.

Jednak, o ile Twoja aplikacja nie jest naprawdę ogromna, lub jesteś naprawdę paranoikiem na temat kolizji nazw zdarzeń, pierwsze trzy opcje powinny wystarczyć.


Nie będę wdawać się w szczegóły o innych środkach komunikacji między twoimi komponentami. Ogólnie rzecz biorąc, gdy sytuacja wymaga udostępniania danych za pomocą zakresu, bezpośredniej interakcji kontrolerów lub komunikacji za pomocą atrybutów węzła DOM, powinieneś o tym wiedzieć.

 15
Author: hon2a,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-29 17:37:24

[[3]}powiedziałbym, że nadawanie jest kanciastym sposobem, jak to osiągnąć.

Jednak twój mediator może działać, jeśli przekażesz wewnętrzną funkcję dyrektywy, w przykładzie użyłem metody na scope, ale można to zrobić również metodą kontrolera.

Użyłem dokładnie tej samej fabryki, Co ty.

angular.module("sharedService", []) 
.factory('MessageService',
  function() {
    var MessageService = {};

    var listeners = {};
    var count = 0;
    MessageService.registerListener = function(listener) {
      listeners[count] = listener;
      count++;

      return (function(currentCount) {
        return function() {
          delete listeners[currentCount];
        };
      })(count);
    };

    MessageService.broadcastMessage = function(message) {
      var keys = Object.keys(listeners);

      for (var i = 0; i < keys.length; i++) {
        listeners[keys[i]](message);
      }
    };

    return MessageService;
  }
)

.directive("directiveA", function(MessageService) {
  return {
    link:function(scope) {
      scope.click = function() {
        MessageService.broadcastMessage("broadcasted message");
      };
    },
    template: '<button ng-click="click()">Click</button>'
  }; 
})
.directive("directiveB", function(MessageService) {
  return {
    link:function(scope) {        
      scope.callback = function(message) {
        console.log(message);
      };

      MessageService.registerListener(scope.callback);
    }
  };
});

Pełny przykład: http://jsbin.com/mobifuketi/1/edit?html, js, console, output

Aby być kompletnym, chciałbym dodać, że angular zapewnia również więcej możliwość komunikowania się dyrektyw.

Require atribute

Jeśli Twoje dyrektywy są połączone w hierarchii, możesz użyć atrybutu require, który pozwala ci uzyskać dostęp do innego kontrolera dyrektyw. Jest to zwykle najlepsze rozwiązanie w wielu przypadkach.

.directive("directiveA", function() {
  return {
    require: "^directiveB",

    link: function(scope, element, attrs, directiveCtrl) {

      scope.click = function() {
        directiveCtrl.call();
      };
    },
    template: '<button ng-click="click()">Click</button>'
  }; 
})
.directive("directiveB", function() {
  return {
    controller :function() {
       this.call = function() {

        console.log("method has been called");
      };
    }
  };
});

Pełny przykład: http://jsbin.com/turoxikute/1/edit?html, js, console, output

Using $watch

Jeśli funkcjonalność zależy od danych, a nie od akcji, korzystasz z $watch i react na zmiany danego modelu lub modelu przechowywanego w usłudze shared service, nie jest podobny do listenera, jego podstawowe sprawdzanie zmian. Nazwałem metodę changeState () i log "state changed", aby wszyscy widzieli to wyraźnie.

angular.module("sharedService", []) 
.service("MediatorService", function() {
  this.state = true;

  this.changeState = function() {
     this.state = !this.state;
  };
})

.directive("directiveA", function(MediatorService) {
  return {
    link:function(scope) {

      scope.click = function() {
        MediatorService.changeState();
      };
    },
    template: '<button ng-click="click()">Click</button>'
  }; 
})

.directive("directiveB", function(MediatorService) {
  return {
    link:function(scope) {
        scope.mediator = MediatorService; 
      scope.$watch("mediator.state", function(oldValue, newValue) {
        if (oldValue == newValue) {
          return;
        }  

        console.log("state changed");
      });
    }
  };
});

Pełny przykład: http://jsbin.com/darefijeto/1/edit?html, js, console, output

 10
Author: Víťa Plšek - angular.cz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-10 20:34:24

Lubię autobus eventowy.

Angular dostarcza $emit na $rootScope, ale nie sądzę, że powinno to wiązać twoją decyzję o użyciu go do przepływów opartych na zdarzeniach, jeśli są one złożone lub przewidywalnie złożone. Angular ma wiele funkcji i chociaż większość z nich jest świetna, nawet autorzy przyznają, że mają one głównie uzupełniać dobre zasady inżynierii oprogramowania, a nie je zastępować.

Lubię ten post na using postal.js: kątowy.js event bus z pocztą.js . The two głównymi korzyściami są kanały i koperty, które pozwolą na bardziej jednoznaczną, zrozumiałą i elastyczną logikę opartą na zdarzeniach.

Uważam, że podejścia oparte na usługach są podatne na błędy, jeśli stan nie jest zarządzany ściśle, co jest trudne w przypadku wywołań asynchronicznych i zastrzyków, gdzie nie można być pewnym, w jaki sposób usługa będzie wielofunkcyjna w przyszłości.

 3
Author: ron pastore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-28 16:25:06