Posted on individual independent blogs, link: http://pengisgood.github.io/2016/01/31/communication-between-multiple-angular-apps/
In general, it is very easy to communicate between different controllers or Service in a single-page application in Angular, because Angular has provided us with some convenient methods: $on
, $emit
$broadcast
.
Here is a simple example to illustrate the use of these three methods, the full version of the code can also refer to here:
Style.css
1 Body {2 background-color: #eee; 3} 4 5 #child {6 background-color:red; 7} 8 9 #grandChild {ten B Ackground-color:yellow;11}12 #sibling { background-color:pink;15}16. Level { Border:solid 1px;19< C12/>margin:5px;20 Padding:5px;21}
Index.html
1 <body ng-app= "app" ng-controller= "Parentctrl" class= ' Level ' > 2 App.js
1 var app = Angular.module (' app ', []) 2 App.controller (' Parentctrl ', function ($scope) {3 $scope. Message = ' 4 5 $s Cope.broadcastmsg = function () {6 $scope. $broadcast (' msg_triggered ', ' Parent ') 7} 8 9 $scope. emitmsg = function ( ) {$scope. $emit (' msg_triggered ', ' parent ')}12 $scope. $on (' msg_triggered ', function (event, from) {$SC Ope.message = from15}) + App.controller (' Childctrl ', function ($scope) {$scope. Message = ' $scope. Broa Dcastmsg = function () {$scope. $broadcast (' msg_triggered ', ' child ')}23 $scope. emitmsg = function () {25 $scope. $emit (' msg_triggered ', ' child ')}27 $scope. $on (' msg_triggered ', function (event, from) {$scope. Message = From30}) app.controller (' Grandchildctrl ', function ($scope) {$scope. Message = "$scope. $on (' ms G_triggered ', function (event, from) {PNS $scope. Message = From38}) (+)-App.controller (' Siblingctrl ', function ($ Scope) {$scope. MessAge = ' $scope. broadcastmsg = function () {$scope. $broadcast (' msg_triggered ', ' sibling ')}46 $scope. EMI Tmsg = function () {$scope. $emit (' msg_triggered ', ' sibling ')}50 $scope. $on (' msg_triggered ', function (event, From) {$scope. Message = from53}) 54})
In the example above, we can see that using some of the APIs available in Angular can easily communicate between different controllers, only broadcast events are required.
The code above works because we always have a premise that these controllers are in the same ng-app. So, what if there are multiple Ng-app in a page? (although this is not recommended, you will still encounter this scenario in real-world projects, especially in some legacy projects.) )
Let's look at a simple example:
Style.css
1. App-container {2 height:200px;3 background-color:white;4 padding:10px;5}6 7 Pre {8 font-size: 20PX;9}
Index.html
1 <body> 2 <div class= "App-container" ng-app= "App1" id= "App1" ng-controller= "Actrl" > 3 < H1>app1
App.js
1 angular 2 . Module (' App1 ', []) 3 . Controller (' Actrl ', function ($scope) {4 $scope. Count = 0; 5 6 $ Scope.increase = function () {7 $scope. Count + = 1; 8 }; 9 }); Angular12 module (' App2 ', []) . c Ontroller (' bCTRL ', function ($scope) { $scope. Count = 0;15 $scope. Increase = function () { $ Scope.count + = 1;18 };19 });
How to start the Angular
Running this code directly, we will find that the second Ng-app does not work, or that the second Ng-app does not start automatically. Why is that? Believe that the Angular know more people will immediately give the answer, that is, Angular will only automatically start to find the first Ng-app, the other Ng-app no chance to start automatically.
How to solve this problem? We can manually start the Ng-app that is not started later. As an example:
Hello_world.html
1 <!doctype html> 2
Manual start requires two points: first, when using the manual start mode, the DOM can no longer use the ng-app instruction, and the second is that manual start does not create a nonexistent module in thin air, so you need to load module-related code before calling the angular.bootstrap
method. If you are still not sure how to start the Angular, please refer to the official documentation.
Now about the angular startup problem solved, perhaps some people will ask, if my page in different places there are many need to manually start the Ng-app what to do? Do I have to call it over and over again angualar.bootstrap
? This kind of code seems to always feel wrong, there are too many duplicate code, so we need to refactor. The way we refactor here can be varied, and the way we do it is this:
Main.js
1 $ (function () {2 $ (' [Data-angular-app] '). each (function () {3 var $this = $ (This) 4 angular.bootstrap ($ This, [$this. attr (' Data-angular-app ')) 5 }) 6})
Change all the Ng-app in the code to Data-angular-app, then use JQuery to parse all the attributes on the DOM at document ready, data-angular-app
get the Ng-app value, and start the angular manually.
Mini pub-subGoing through a hole, and we go back to another question, how can we communicate in multiple Ng-app? After all, they are no longer in the same context. It is to be explained here that Ng-app in Angular cannot have nested relationships in the DOM structure. Each ng-app has its own rootscope, and we can no longer directly use some of the APIs that Angular provides. Because either way $broadcast
$emit
, they can't cross different ng-app. People who believe that the release subscription mechanism (especially those who have done the WinForm program) can quickly come up with a workable solution, that is, we implement a simple publish-subscribe mechanism ourselves, and then communicate in different Ng-app by publishing subscription-customized events.
It sounds simple, actually it's easy to do.Talk is cheap, show me the code.
First we need a place to manage the event, a detailed explanation [refer to this post on StackOverflow (http://stackoverflow.com/a/2969692/3049524).
Event_manager.js
1 (function ($) {2 var EventManager = $ ({}) 3 4 $.subscribe = function () {5 eventManager.bind.apply ( EventManager, FN) 6 } 7 8 $.publish = function () {9 eventManager.trigger.apply (EventManager, FN) 10 }11}) (JQuery)
Only two APIs are implemented for the time being, one subscribe
for subscribing to events and publish
for publishing events.
Subscribe to Events:
1 $.subscribe (' user_rank_changed ', function (event, data) {2 $timeout (function () {3 //Do Something4 }) 5})
To publish an event:
1 $.publish (' user_rank_changed ', {/*some data*/})
Here is a small trick, because our event publishing subscription is the jQuery approach, in order for Angular to be able to perceive the changes in the scope of the data, we have the entire callback function wrapped in $timeout
, by JavaScript Put yourself in the time loop and wait until the idle time to start executing, rather than using the $scope.$apply()
method, because sometimes calling the method directly gives us another Error: $digest already in progress
error. Although it can also be $rootScope.$$phase || $rootScope.$apply();
avoided in this way, but personally think it is slightly tricky, no $timeout
way elegant.
Because we use native JavaScript event mechanisms, we can easily transfer data to and from each other even if our Controller or Service is in a different ng-app.
Principles of improvementIn angular's single-page application, we try to use only one ng-app for one application, then divide the business by module, not Ng-app. Not the last resort, do not mix with jQuery, always use Angular thinking way to develop, or accidentally will fall into the data out of sync pit.
Turn
Communication between Controllers & Services in multiple Ng-app