简体   繁体   中英

AngularJS trigger and watch object value change in service from controller from another module (Extended)

Referring to this question AngularJS trigger and watch object value change in service from controller

It is trying to watch for changes in a service from a controller. Im trying to extend it to handle multiple module (concurrent app in same page's different div's) communication.

Problem:

I want to achieve the similar feat, but with slight different scenario. I have two modules myApp and yourApp for example. myApp has a service. I want to watch changes in myApp's service within yourApp module's controller. Can it be done? or is there different method to reflect and detect data changes of one module inside another module.

Consider the example code below:

HTML

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
        <div ng-click="setFTag()">Click Me</div>
    </div>
</div>
<div ng-app="yourApp">
    <div ng-controller="yourCtrl">       
    </div>
</div>

Javascript

// myApp module
var myApp = angular.module('myApp',[]);

// myApp's service
myApp.service('myService', function() {
    this.tags = {
        a: true,
        b: true
    };

    this.setFalseTag = function() {
        alert("Within myService->setFalseTag");
        this.tags.a = false;
        this.tags.b = false;

    //how do I get the watch in YourCtrl of yourApp module to be triggered?
    };
});

// myApp's controller
myApp.controller('MyCtrl', function($scope, myService) {

    //$scope.myService = myService;    
    $scope.setFTag = function() {
        alert("Within MyCtrl->setFTag");
        myService.setFalseTag();
    };        

    /*
    $scope.$watch(function () {
        return myService.tags;
    }, function(newVal, oldVal) {
        alert("Inside watch");
        console.log(newVal);
        console.log(oldVal);
    }, true);  
    */
});

// yourApp module (Injecting myApp module into yourApp module)
var yourApp = angular.module('yourApp',['myApp']);
// yourApp's controller
yourApp.controller('YourCtrl', function($scope, myService) {

    $scope.$watch(function () {
        return myService.tags;
    }, function(newVal, oldVal) {
        alert("Inside watch of yourcontroller");
        console.log(newVal);
        console.log(oldVal);
    }, true);

});

Note: I am not sure if this is the right way to communicate between modules. Any suggestion or the solution will be highly appreciated.

PS Ive bootstrapped two modules to fit into same page.

JSFiddle

Communicating between modules is a whole different story from communicating between apps.

In the case of communicating between modules , it's only a matter of injecting module A into module B.

In which case, module B has complete access to inject/communicate with anything exposed in module A.


Communicating between apps however, is somewhat more complicated. You would need to setup something 'outside' the angular world, accepting properties from both applications.

I've put together a jsBin , showcasing how you can do it.

With that said, I'm not sure I would recommend it - but then again best practices on the subject do not exist afaik.

The gist of it is;

  1. Setup a new instance of a shared service that lives outside the Angular world.
  2. Attach the instance of said service to window .
  3. Access the above instance in your app specific services/controllers through $window .
  4. Register each $scope that needs to access the data stored in the shared service.
  5. Upon updating data in the shared service, loop through the registered $scopes and trigger an $evalAsync (so as to not end up with $digest already in progress ).
  6. Watch as your data is synced across applications.

This is just a PoC on how to do it, I would not recommend it as it sort of blows when it comes to unit testing. And also because we are exposing properties on window . yuck.

Sure, you could disregard from exposing on window - but then your code would have to live in the same file ( afaic ). Yet another, yuck.


To build upon this, if you were to decide (or, be able to) only use a single app (multiple modules is just fine) and want to communicate/sync a service with multiple components I reckon this would be the best way to do so:

app.service('shared', function () {
  var data = {
      a: true,
      b: true
  };

  this.toggle = function() {
    data.a = !data.a;
    data.b = !data.b;
  };

  Object.defineProperty(this, 'data', {
      get: function () {
          return data;
      }
  });      
});

By using an object getter , you won't need to setup a $watch to sync data across your components within the same module. Nor trigger manual $digest 's.

another jsbin - showcasing the above.


The difference between two modules and two apps as I see it:

2 modules

Two separate modules that gets 'bundled' together into a single application (either by a third module, or injecting module A into B).

var app1 = angular.module('app1', ['app2', 'app3']);

var app2 = angular.module('app2', []);

var app3 = angular.module('app3', []);

angular.bootstrap(/*domElement*/, app1);

2 Apps

var app1 = angular.module('app1', []);
var app2 = angular.module('app2', []);

angular.bootstrap(/*domElement1*/, app1);
angular.bootstrap(/*domElement2*/, app2);

I don't think there is any point in having two applications and share state between the two. I think the whole point with running two applications is to separate the two. Otherwise it's just over engineering imho .

Some thoughts:

  • You could add a common dependency to both applications. It probably wont be a shared state, but you would have access to the same implementation in both apps.
  • You could possibly utilise sessionStorage as a medium of transportation for your data between the two applications. Just make sure to cleanup afterwards :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM