【问题标题】:How to communicate between controllers while not using SharedService between them?如何在控制器之间进行通信而不使用它们之间的 SharedService?
【发布时间】:2014-08-18 13:05:55
【问题描述】:

我正在阅读有关控制器和指令之间通信的所有答案,但在我看来,这似乎是使用共享服务并将其注入其中的每一个。当我开发一个非常大规模的应用程序时,我不知道我的页面将包含什么内容。我可能有 2 个控制器需要在它们之间进行通信,并且我也可能在同一页面中有 5 个指令和 2 个控制器。我不知道从头开始我的视图/页面中会出现什么,所以我需要一种更好的方式来在它们之间进行通信。我正在寻找更好的方法,正确的 AngularJS 方法。

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

上面是我正在处理的视图示例: 在左侧我有一个指令树,在右侧我有图表控制器和指令网格。我可能拥有的不止这些,但这是我可能拥有的一个很好的例子。请记住,视图可以包含 X 个组件,您从一开始就不知道其中会包含什么。

现在,假设每次我选择左侧树中的节点时,我希望能够告诉其他控制器 nodeSelectedChange 事件发生了。我不想为它们中的每一个注入一个保存该信息的服务,所以我想到了拥有一个页面管理器控制器之类的东西,它是所有视图的父亲。我页面内的所有控制器/指令只能通过 PageManagerController 相互通信,他是页面中唯一知道他在其中拥有什么的东西。

这里要记住的重要一点:树不知道页面有图表或网格,它们不需要相互了解就可以进行交流。页面管理器什么都知道,现在我想让它发挥作用——它应该有服务吗?其他组件是否应该有服务并且服务可以与PageManager服务对话?

帮助我思考。希望我能把所有的点联系起来,创造一种更好的沟通方式。 我认为 sharedService 方式适用于小型应用程序,当您从一开始就知道应用程序中发生了什么,但大多数时候 - 您只是不知道谁以及何时使用您的指令,它应该能够与大家交谈。

我不喜欢活动的地方:

  1. 在控制器内部触发的事件,而它应该在服务内部。
  2. 监听器控制器也应该知道他要监听的事件的名称。如果我将事件名称更改为 $emit 或 $broadcast,我需要遍历所有应用程序中的所有侦听器 $on("eventName") 并更改为该唯一名称。
  3. 指令就像一个黑匣子,我不想每次都检查它的内部并找到他正在广播的事件的名称以便与之通信。

我需要一种方法将事件 NAMES 从指令中暴露出来,可能使用连接到该控制器的服务。

【问题讨论】:

    标签: angularjs angularjs-directive angularjs-scope angular-ui


    【解决方案1】:

    有几个好的做法:

    1. 使用$scope 在指令和控制器之间为runtime 依赖项进行通信(例如,您从服务器获取的模型、实例化的类等)。要走的路是有一个隔离范围,其属性映射到依赖项:

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

      像这样使用它:

      <tree somevalue="objectFromController">
      
    2. 使用注入的服务静态依赖(Presentation Models、全局共享状态等)进行通信

      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. 如果您想拥有最大的可组合性,请坚持第一种模式:

      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
      });
      

      并使用模板和绑定作为通信总线来组合这些东西:

      <tree model="treeModel">
      

    坚持这种模式,你会得到:

    1. 没有事件
    2. 定义明确的指令“接口”(隔离范围内的属性)
    3. 易于组合
    4. 基于范围更改传播的反应性行为

    【讨论】:

      【解决方案2】:

      这实际上取决于您希望从树指令共享到页面其他部分的信息类型。

      你有几个选择:

      使用范围事件

      正如上面提到的,您可以触发一个事件并监听各种控制器和/或服务中的事件。也就是说,它会变得非常丑陋,非常快。跟踪事件并找出在给定时间点处于活动状态的事件侦听器可以让最优秀的工程师头痛!

      使用服务

      另一种选择是使用服务,比如 PageManagerService。在这种情况下,

      • 每次单击树项都会在 PageManagerService 上设置一些信息,说明需要显示哪个页面、哪些对象以及需要显示哪些项
      • 每个需要更改的组件都可以
        • 注册一个监听器,以便在 PageManagerService 更改时触发
        • 在服务上添加监视并在 PageManagerService 更改时运行其代码

      服务本身只是共享状态,它取决于指令和组件如何使用和响应状态的变化。

      使用 UI 路由器

      但我越想这个,它就越像是 UI Router 之类的好用例。 UI Router 允许您定义状态,并让页面的不同部分以不同的方式响应状态变化。每个部分都可以通过加载以自己的方式响应状态变化

      • 不同的控制器
      • 不同的模板,可能包含不同的组件和小部件

      所以你最终会得到一个结构

      • 左侧的 Tree 指令
      • 一个名为 top 的 ui 视图
      • 一个名为底部的 ui 视图

      你的树指令中的每个项目都可以是一个 ui-sref,这只是一种奇特的说法,而不是重定向到一个 URL,而是重定向到一个状态。

      然后您可以在应用程序中的一个地方定义您的配置,如下所示:

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

      同样,您可以在树指令中为每个项目定义一个状态,每个项目只是指向不同状态的链接。

      当然,状态定义是在 AngularJS 应用程序的 config 部分中完成的,您会在应用程序启动之前知道这一点。如果您也需要动态状态怎么办?

      好吧,一些答案/想法也可以引导您了解这一点:

      • 必须预定义控制器和服务,您不会动态创建 HTML 内容和/或 JS 控制器
      • 可以在配置部分将 $stateProvider 公开为全局变量,并在控制器/服务中动态调用 stateProvider.state,只要您有新的状态定义。
      • 尽管如此,控制器和 HTML 通常保持不变,我们只需要使用各种参数触发不同的状态。这可以通过调用 transitionTo 函数以转换到具有各种状态参数的已定义状态来轻松完成。

      【讨论】:

        【解决方案3】:

        您可以使用 $on、$emit 和 $broadcast 在不同的控制器/作用域之间进行通信。

        请关注我之前的一篇文章。我有一个设置 plunk 。你可以试试这个例子。 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
        

        要了解更多信息,请访问 - https://docs.angularjs.org/api/ng/type/$rootScope.Scope

        【讨论】:

        • 这个解决方案是有风险的 - 控制器触发事件​​(任何名称)并且外部人员必须知道该事件的名称才能注册它。这是我想避免的事情,使用硬编码的事件名称。
        • Aviv M - 我也想避免这种情况。我喜欢 Mr_Mig 的回答,并认为我将选择共享服务,因为在我的情况下可组合性不是问题,因为我在 3rd 方库上调用函数,而不是更改状态。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-31
        • 1970-01-01
        • 2017-04-26
        • 2016-04-14
        • 2019-04-20
        • 2023-04-06
        • 2012-08-10
        相关资源
        最近更新 更多