How to communicate between controllers while not using SharedService between them?

跟風遠走 提交于 2019-12-25 03:55:35

问题


I was reading all the answers about communication between controllers and directive, but is seems to me occurred using shared service and inject it to each one of them. When I'm developing a very large scale application, I don't know what my page is going to have in it. I may have 2 controllers need to communicate between them, and I also may have 5 directive and 2 controllers in the same page. I don't know from scratch what is going to be inside my view/page, so I need a better way how to communicate between them. I'm looking for that better way to do it, the right AngularJS way.

http://i60.tinypic.com/2z87q05.png

Above is my view example I'm working on: on the left side I have a directive tree, on the right side I have chart controller and directive grid. I may have more than this, but this is a good example of what I may have. Keep in mind, the view can have X componenets, you don't know from the beginning what will be in it.

Now, lets say each time I select node in tree on the left, I want to be able to tell the other controllers that nodeSelectedChange event happend. I don't want to inject each one of them a service that holds that info, so I thought about something like having a Page Manager Controller, which is the father of the all view. All my controllers/directives inside my page should talk with each other only by the PageManagerController, he is the only thing in page that knows what he has inside of it.

Important here to keep in mind: The tree don't know the page has chart or grid, they don't need to know each other in order to communicate. The page manager knows everything, now I want it to make the magic - Should it has a service? Should each other component has service and service can talk with PageManager service?

Help me to think. Hope I can connect all the dots to create a BETTER way for communication. I think the sharedService way is good for small app, when you know from start what is going on in your app, but most of the time - You just don't know who and when is going to use your directive, it should be able to talk with everyone.

What I don't like about events:

  1. The events being fired inside the controller, while it should be inside a service.
  2. The listener controller should know the name of the event he is going to listen too. If I change the event name being $emit or $broadcast, I need to go all over the listeners $on("eventName") in all app and change to that unique name.
  3. Directive is like a black box, I don't want to check inside of it each time and find the names of the events he is being broadcasting in order to communicate with it.

I need a way to exposed the events NAMES out of the directive, probably with a service connected to that controller.


回答1:


It really depends on the type of information you want to share from the tree directive to the other sections of the page.

You have a few options in front of you:

Use Scope Events

As someone mentioned above, you could fire an event and listen for events in various controllers and/or services. That said, it can get really ugly, really fast. Tracing events and figuring out what event listeners are active at a given point can give the best engineers a migraine!

Use a Service

Another option would be to use a Service, let's say a PageManagerService. In that case,

  • Each click of a tree item would set some information on the PageManagerService, saying which page, what objects, and what items it needs to display
  • Each component that needs to change could
    • Register a listener to be triggered when the PageManagerService changes
    • Add a watch on the service and run its code when the PageManagerService changes

The service itself is just going to be shared state, and it would be upto the directives and components on how it wants to consume and respond to changes in the state.

Use UI Router

But the more I think about this, the more it seems like a good use case from something like UI Router. UI Router allows you to define states, and have different parts of the page respond in different ways to state changes. Each section could respond to a state change in its own way, by loading

  • A different controller
  • A different template, possibly with different components and widgets

So what you would end up having is a structure with

  • The Tree directive on the left
  • A ui-view named, let's say, top
  • A ui-view named, let's say, bottom

Each item in your tree directive can then be a ui-sref, which is just a fancy way of saying instead of redirecting to a URL, redirect to a state.

You could then define your configurations in your application in a single place, like so:

$stateProvider.state('dashboard', {
  views: {
    "top": { templateUrl: 'my/dashboard.html', controller: 'DashboardCtrl'}
    "bottom": { templateUrl: 'my/dashboard-grid.html', controller: 'DashboardGridCtrl'}
  }
})

Similarly, you could have a state definition for each item in your tree directive, and each one just be a link to a different state.

Of course, the state definitions are done in your config section in an AngularJS application, which you would know is before the application starts. What if you needed dynamic states as well?

Well, a few answers / thoughts to lead you down the way for that as well:

  • The controllers and services would have to be predefined, you would not be dynamically creating HTML content and/or JS controllers
  • It is possible to expose the $stateProvider as a global variable in the config section, and dynamically call stateProvider.state inside a controller / service, wherever you have new state definitions.
  • More often than not though, the controllers and HTML remain constant, and we just need to trigger the different states with various parameters. That can easily be done by calling transitionTo function to transition to a defined state with various state parameters.



回答2:


There are a couple of good practices:

  1. Use the $scope to communicate between directives and controllers for runtime dependencies (e.g. models you fetch from the server, instantiated classes, etc.). The way to go is to have an isolated scope with attributes mapped to the dependencies:

    directive('tree', function(){
      return {
        scope: {
          somevalue : "="
        }
      }
    });
    

    Use it like this:

    <tree somevalue="objectFromController">
    
  2. Use the injected services to communicate with static dependencies (Presentation Models, global sharable state, etc.)

    directive('tree', function(treeState){
      return {
        scope: {
          somevalue : "="
        },
        link: function(scope){
          // some logic updating the treeState
          treeState.openNodes = ['x', 'y'];
        }
      }
    });
    
    controller('ctrl', function($scope, treeState){
      // react to treeState changes
      // you can use $scope.$watch for it
      // or any other way you like
      // see: https://github.com/mr-mig/angular-react-to
    
    });
    
  3. If you want to have maximum composability, stick to the first pattern:

    directive('tree', function(){
      return {
        scope: {
          model : "="
        },
        link: function(scope){
          // some logic updating the treeState, stored as scope.model
          scope.model.openNodes = ['x', 'y'];
        }
      }
    });
    
    controller('ctrl', function($scope, treeFactory){
      $scope.treeModel = treeFactory.create();
      // react to treeState changes
      // you can use $scope.$watch for it
      // or any other way you like
      // see: https://github.com/mr-mig/angular-react-to
    });
    

    And compose this stuff using template and binding as a communication bus:

    <tree model="treeModel">
    

Sticking to this pattern you get:

  1. No events
  2. Well-defined directive "interface" (attributes in the isolated scope)
  3. Easy composability
  4. Reactive behavior based on scope change propagation



回答3:


you can use $on, $emit and $broadcast to communicate among different controllers/scopes .

Please follow one of my earlier post . I have a setup a plunk . You can try out the example. Angular Js newbie - link in a controller view that triggers another controller action

$on : setup a event handler
$emit : communicate to parent controllers
$broadcast : communicate to child controllers

To know more visit - https://docs.angularjs.org/api/ng/type/$rootScope.Scope



来源:https://stackoverflow.com/questions/25364061/how-to-communicate-between-controllers-while-not-using-sharedservice-between-the

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!