Fast jeder auf der Welt fragt jetzt! Ist da draußen jemand? Wir sind in Schwierigkeiten, unsere Dienste sprechen Klingonisch und unsere Controller können nicht mit ihnen kommunizieren. Kann uns jemand helfen?
Ich kann Ihnen nicht sagen, wie oft mir diese Frage gestellt wurde, wie Komponenten in AngularJS am besten kommunizieren können. Oft besteht die Antwort darin, ein $rootScope-Objekt zu verwenden, um jeden zu erreichen, der zuhören möchte . Broadcast $broadcast eine Nachricht. Das ist jedoch nicht der beste Weg, Nachrichten zwischen Komponenten zu senden, was ihre Modularität und Wiederverwendung einschränktIn diesem Artikel zeige ich Ihnen, wie Sie das Publish/Subscribe-Muster für die interne Komponentenkommunikation in AngularJS verwenden.
AngularJS bietet viele Möglichkeiten zur Kommunikation zwischen Komponenten. Bei den am häufigsten verwendeten Methoden müssen Sie jedoch zu viele Details über die Kommunikation dieser Komponenten kennen, was die Kopplung zwischen Komponenten erhöht und ihre Modularität und Kohäsion verringert. Dies erschwert die Wiederverwendung Ihre Komponenten in anderen Anwendungen.
Durch die Verwendung des Publish/Subscribe-Entwurfsmusters können wir die Kopplung zwischen Komponenten reduzieren und die Details ihrer Kommunikation kapseln. Dies wird dazu beitragen, die Modularität, Testbarkeit und Wiederverwendbarkeit Ihrer Komponenten zu erhöhen.
Die Implementierung des Publish/Subscribe-Musters, die ich beschreiben werde, wurde von Eric Burley, @eburley in seinem Beitrag
angularjs.org Observations, About the Publish/Subscribe Pattern..empfohlen. Die von mir beschriebene Beispielanwendung zeigt Ihnen, wie Sie das Publish/Subscribe-Muster für die interne Controller-Kommunikation und Controller-Service-Kommunikation verwenden können. Sie finden es in meinem Repository auf GitHub
angularjs-pubsubFinden Sie die Quelle Code unten.
Zuerst brauchen wir einen Kommunikationskanal
Lassen Sie uns zunächst über die Dienste sprechen, die für die Verarbeitung von Veröffentlichungs- und Abonnementinformationen verwendet werden. Ich habe eine Serviceschnittstelle definiert, die Methoden zum Veröffentlichen und Abonnieren von Informationen bereitstellt, mit denen wir die Informationen verarbeiten können, die wir austauschen möchten.
Im folgenden Code definiere ich zwei interne Nachrichten: _EDIT_DATA_, um anzuzeigen, dass wir die mit der Nachricht übergebenen Daten bearbeiten müssen, und _DATA_UPDATED_, um anzuzeigen, dass unsere Daten geändert wurden. Diese werden intern definiert und sind für Benutzer nicht zugänglich, wodurch die Implementierung verborgen bleibt.
Für jede Information gibt es zwei Methoden; eine wird verwendet, um die Informationen zu veröffentlichen und an Abonnenten weiterzuleiten, und die andere ermöglicht es Abonnenten, eine Rückrufmethode zu registrieren, die aufgerufen wird, wenn die Informationen empfangen werden.
Die zum Veröffentlichen von Informationen für Abonnenten verwendeten Methoden sind editData in Zeile 9 und dataUpated in Zeile 19. Sie senden private Benachrichtigungen an ausstehende Ereignisse über die Methode $rootScope.$broadcast.
Die zum Registrieren von Ereignissen verwendete Methode besteht darin, über $scope.$on einen Listener einzurichten. Nach dem Empfang der Broadcast-Nachricht werden die vom Abonnenten des Dienstes registrierten Ereignisse der Reihe nach ausgeführt. Da der Abonnent gleichzeitig einen eigenen Bereich benötigt, der als Parameter übergeben werden muss, können wir ihn gleichzeitig zum Ausführen der überwachten Informationen verwenden und so die komplizierte Verarbeitung der Pflege der Listener-Liste vermeiden. Die Methoden zum Registrieren des Ereignisses sind onEditData in Zeile 13 und onDataUpdated in Zeile 23.
Um Implementierungsdetails auszublenden, habe ich das Muster „Revealing Module“ (enthüllendes Modul: hässlicher Name) verwendet, um nur die Methoden zurückzugeben, die Benutzer verwenden sollen.
angular.module(['application.services']) // define the request notification channel for the pub/sub service .factory('requestNotificationChannel', ['$rootScope', function ($rootScope) { // private notification messages var _EDIT_DATA_ = '_EDIT_DATA_'; var _DATA_UPDATED_ = '_DATA_UPDATED_'; // publish edit data notification var editData = function (item) { $rootScope.$broadcast(_EDIT_DATA_, {item: item}); }; //subscribe to edit data notification var onEditData = function($scope, handler) { $scope.$on(_EDIT_DATA_, function(event, args) { handler(args.item); }); }; // publish data changed notification var dataUpdated = function () { $rootScope.$broadcast(_DATA_UPDATED_); }; // subscribe to data changed notification var onDataUpdated = function ($scope, handler) { $scope.$on(_DATA_UPDATED_, function (event) { handler(); }); }; // return the publicly accessible methods return { editData: editData, onEditData: onEditData, dataUpdated: dataUpdated, onDataUpdated: onDataUpdated }; }])
Das Veröffentlichen einer Nachricht ist einfach. Zuerst müssen wir einige Abhängigkeiten für requestNotificationChannel in unserem Controller einführen. Sie können dies in der zweiten Zeile der Definition von dataService unten sehen, wenn Sie wissen möchten, welche Änderungen vorliegen aufgetreten, Um Signale an andere Objekte zu senden, müssen Sie nur die entsprechende Benachrichtigungsmethode bei requestNotificationChannel aufrufen. Wenn Sie die Methoden saveHop, deleteHop und addHop von dataService bemerken, werden Sie feststellen, dass sie alle die dataUpdated-Methode bei requestNotificationChannel aufrufen Das Signal wird an den Listener gesendet, der mit der onDataUpdated-Methode registriert wurde.
接收事件通知
从 requestNotificationChannel 接收事件通知也很简单,额外的我们只需要回调处理器来在消息被发送时使用通知来做一些自己的处理. 我们将再次需要添加一些依赖到面向我们的控制器、服务以及指令的 requestNotificationChannel 上, 你可以在下面代码的第二行中看到这些. 接下来我们需要定义一个事件回调处理器来对事件通知做出回应,你可以在下面的第五行代码中看到. 然后我们需要通过调用 onDataUpdated 方法来吧我们的回调处理器注册到requestNotificationChannel,并传入来自控制器和回调处理器的范围, 我们在第9行代码中做了这些事情.
//define the controller for view1 .controller('view1-controller', ['$scope', 'dataService', 'requestNotificationChannel', function($scope, dataService, requestNotificationChannel) { $scope.hops = dataService.getHops(); var onDataUpdatedHandler = function() { $scope.hops = dataService.getHops(); } requestNotificationChannel.onDataUpdated($scope, onDataUpdatedHandler); $scope.onEdit = function(hop) { requestNotificationChannel.editData(hop); } $scope.onDelete = function(hop) { dataService.deleteHop(hop); } }]);
用于控制器通信的控制器
我们也可以将 the requestNotificationChannel 用于控制器间的通信. 我们只需要有一个控制器扮演发布者的角色,而另外一个控制器扮演订阅者的角色就行了. 如果你观察到前段代码第11行view1-controller的onEdit方法,你会看到它发送了一个editData消息,消息包含需要使用 requestNotificationChannel 编辑的项. 下面的 view2-controller 从第5行到第9行将它的 onEditDataHandler 用 requestNotificationChannel 进行了注册. 如此无论何时view1-controller一旦发送editData消息,带上要修改的项,view2-controller都会受到editData消息的通知,获得该项并将其更新到它的模型.
//define the controller for view1 .controller('view2-controller', ['$scope', 'dataService', 'requestNotificationChannel', function($scope, dataService, requestNotificationChannel) { $scope.hop = null; var onEditDataHandler = function(item) { $scope.hop = item; }; requestNotificationChannel.onEditData($scope, onEditDataHandler); $scope.onSave = function() { dataService.saveHop($scope.hop); $scope.hop = null; } $scope.onCancel = function() { $scope.hop = null; } }]);
写一个好的接口文档
有一件事情可能会被忽略,我们在组件间用了通信接口,而这些接口,它们需要一个好的文档来说明应当如何使用。上面的例子中,如果没有文档,用户肯定不会知道 onEditData 会给回调函数传一个待编辑数据。所以当你开始用这个模式,用好的技巧在于,给方法写注释文档,以确保通知服务明确知道发生了什么事情。
总结
好了,我们探讨了如何在你的 AngularJS 应用中使用订阅/发布模式来实现模块间通信。该模式可以让你的模块从内部消息解耦,更便于复用。你甚至可以把模块之间的通信全部替换成订阅/发布模式。尤其当你的服务中有很多异步请求,以及你希望把数据缓存在服务中,从而减少和服务器通信的时候,这种模式相当有效。
我希望这对你有所帮助,你可以在我的 GitHub 仓库 angularjs-pubsub 下找到例子的代码。