首页 文章

一个AngularJS控制器可以调用另一个吗?

提问于
浏览
560

是否有可能让一个控制器使用另一个?

例如:

此HTML文档只是打印 MessageCtrl 文件中 MessageCtrl 控制器传递的消息 .

<html xmlns:ng="http://angularjs.org/">
<head>
    <meta charset="utf-8" />
    <title>Inter Controller Communication</title>
</head>
<body>
    <div ng:controller="MessageCtrl">
        <p>{{message}}</p>
    </div>

    <!-- Angular Scripts -->
    <script src="http://code.angularjs.org/angular-0.9.19.js" ng:autobind></script>
    <script src="js/messageCtrl.js" type="text/javascript"></script>
</body>
</html>

控制器文件包含以下代码:

function MessageCtrl()
{
    this.message = function() { 
        return "The current date is: " + new Date().toString(); 
    };
}

这只是打印当前日期;

如果我要添加另一个控制器 DateCtrl ,它将日期以特定格式交回 MessageCtrl ,那怎么会这样做呢? DI框架似乎与 XmlHttpRequests 和访问服务有关 .

13 回答

  • 31

    实际上使用发射和广播是低效的,因为事件在范围层次结构中上下波动,这很容易降级为复杂应用程序的性能瓶装 .

    我建议使用服务 . 以下是我最近在我的一个项目中实现它的方法 - https://gist.github.com/3384419 .

    基本思路 - 将pub-sub / event总线注册为服务 . 然后在需要订阅或发布事件/主题的地方注入该事件总线 .

  • 1

    在角度1.5中,这可以通过执行以下操作来完成:

    (function() {
      'use strict';
    
      angular
        .module('app')
        .component('parentComponent',{
          bindings: {},
          templateUrl: '/templates/products/product.html',
          controller: 'ProductCtrl as vm'
        });
    
      angular
        .module('app')
        .controller('ProductCtrl', ProductCtrl);
    
      function ProductCtrl() {
        var vm = this;
        vm.openAccordion = false;
    
        // Capture stuff from each of the product forms
        vm.productForms = [{}];
    
        vm.addNewForm = function() {
          vm.productForms.push({});
        }
      }
    
    }());
    

    这是父组件 . 在这里我创建了一个函数,将另一个对象推入我的 productForms 数组 - 注意 - 这只是我的例子,这个函数可以是真正的 .

    现在我们可以创建另一个将使用 require 的组件:

    (function() {
      'use strict';
    
      angular
        .module('app')
        .component('childComponent', {
          bindings: {},
          require: {
            parent: '^parentComponent'
          },
          templateUrl: '/templates/products/product-form.html',
          controller: 'ProductFormCtrl as vm'
        });
    
      angular
        .module('app')
        .controller('ProductFormCtrl', ProductFormCtrl);
    
      function ProductFormCtrl() {
        var vm = this;
    
        // Initialization - make use of the parent controllers function
        vm.$onInit = function() {
          vm.addNewForm = vm.parent.addNewForm;
        };  
      }
    
    }());
    

    这里子组件正在创建对父组件函数 addNewForm 的引用,然后可以将其绑定到HTML并像其他任何函数一样调用 .

  • 52

    以下是 publish-subscribe 方法,与Angular JS无关 .

    Search Param Controller

    //Note: Multiple entities publish the same event
    regionButtonClicked: function () 
    {
            EM.fireEvent('onSearchParamSelectedEvent', 'region');
    },
    
    plantButtonClicked: function () 
    {
            EM.fireEvent('onSearchParamSelectedEvent', 'plant');
    },
    

    Search Choices Controller

    //Note: It subscribes for the 'onSearchParamSelectedEvent' published by the Search Param Controller
    localSubscribe: function () {
            EM.on('onSearchParamSelectedEvent', this.loadChoicesView, this);
    
    });
    
    
    loadChoicesView: function (e) {
    
            //Get the entity name from eData attribute which was set in the event manager
            var entity = $(e.target).attr('eData');
    
            console.log(entity);
    
            currentSelectedEntity = entity;
            if (entity == 'region') {
                $('.getvalue').hide();
                this.loadRegionsView();
                this.collapseEntities();
            }
            else if (entity == 'plant') {
                $('.getvalue').hide();
                this.loadPlantsView();
                this.collapseEntities();
            }
    
    
    });
    

    Event Manager

    myBase.EventManager = {
    
        eventArray:new Array(),
    
    
        on: function(event, handler, exchangeId) {
            var idArray;
            if (this.eventArray[event] == null) {
                idArray = new Array();
            } else { 
                idArray = this.eventArray[event];
            }
            idArray.push(exchangeId);
            this.eventArray[event] = idArray;
    
            //Binding using jQuery
            $(exchangeId).bind(event, handler);
        },
    
        un: function(event, handler, exchangeId) {
    
            if (this.eventArray[event] != null) {
                var idArray = this.eventArray[event];
                idArray.pop(exchangeId);
                this.eventArray[event] = idArray;
    
                $(exchangeId).unbind(event, handler);
            }
        },
    
        fireEvent: function(event, info) {
            var ids = this.eventArray[event];
    
            for (idindex = 0; idindex < ids.length; idindex++) {
                if (ids[idindex]) {
    
                    //Add attribute eData
                    $(ids[idindex]).attr('eData', info);
                    $(ids[idindex]).trigger(event);
                }
            }
        }
    };
    

    Global

    var EM = myBase.EventManager;
    
  • 2

    我也知道这种方式 .

    angular.element($('#__userProfile')).scope().close();
    

    但是我没有太多使用它,因为我不喜欢在角度代码中使用jQuery选择器 .

  • 3

    以下是两个控制器共享服务数据的一页示例:

    <!doctype html>
    <html ng-app="project">
    <head>
        <title>Angular: Service example</title>
        <script src="http://code.angularjs.org/angular-1.0.1.js"></script>
        <script>
    var projectModule = angular.module('project',[]);
    
    projectModule.factory('theService', function() {  
        return {
            thing : {
                x : 100
            }
        };
    });
    
    function FirstCtrl($scope, theService) {
        $scope.thing = theService.thing;
        $scope.name = "First Controller";
    }
    
    function SecondCtrl($scope, theService) {   
        $scope.someThing = theService.thing; 
        $scope.name = "Second Controller!";
    }
        </script>
    </head>
    <body>  
        <div ng-controller="FirstCtrl">
            <h2>{{name}}</h2>
            <input ng-model="thing.x"/>         
        </div>
    
        <div ng-controller="SecondCtrl">
            <h2>{{name}}</h2>
            <input ng-model="someThing.x"/>             
        </div>
    </body>
    </html>
    

    也在这里:https://gist.github.com/3595424

  • 2

    有一种方法不依赖于服务, $broadcast$emit . 它并不适用于所有情况,但如果您有2个可以抽象为指令的相关控制器,那么您可以在指令定义中使用 require 选项 . 这很可能是ngModel和ngForm之间的沟通方式 . 您可以使用它在嵌套的指令控制器之间或在同一元素上进行通信 .

    对于父母/子女情况,使用方法如下:

    <div parent-directive>
      <div inner-directive></div>
    </div>
    

    让它工作的要点:在父指令上,使用要调用的方法,你应该在 this 上定义它们(不在 $scope 上):

    controller: function($scope) {
      this.publicMethodOnParentDirective = function() {
        // Do something
      }
    }
    

    在子指令定义中,您可以使用 require 选项,以便将父控制器传递给链接函数(这样您就可以从子指令的 scope 调用函数) .

    require: '^parentDirective',
    template: '<span ng-click="onClick()">Click on this to call parent directive</span>',
    link: function link(scope, iElement, iAttrs, parentController) {
      scope.onClick = function() {
        parentController.publicMethodOnParentDirective();
      }
    }
    

    上面的内容可以在http://plnkr.co/edit/poeq460VmQER8Gl9w8Oz?p=preview看到

    类似地使用兄弟指令,但两个指令在同一元素上:

    <div directive1 directive2>
    </div>
    

    通过在 directive1 上创建方法来使用:

    controller: function($scope) {
      this.publicMethod = function() {
        // Do something
      }
    }
    

    在directive2中,可以使用 require 选项调用它,这会导致siblingController传递给链接函数:

    require: 'directive1',
    template: '<span ng-click="onClick()">Click on this to call sibling directive1</span>',
    link: function link(scope, iElement, iAttrs, siblingController) {
      scope.onClick = function() {
        siblingController.publicMethod();
      }
    }
    

    这可以在http://plnkr.co/edit/MUD2snf9zvadfnDXq85w?p=preview看到 .

    这个的用途?

    • Parent:子元素需要与父级“注册”的任何情况 . 很像ngModel和ngForm之间的关系 . 这些可能会添加某些可能影响模型的行为 . 你可能也有纯粹基于DOM的东西,其中父元素需要管理某些孩子的位置,比如管理或反应滚动 .

    • 兄弟姐妹:允许指令修改其行为 . ngModel是经典案例,用于在输入上添加解析器/验证到ngModel使用 .

  • 49

    看到这个小提琴:http://jsfiddle.net/simpulton/XqDxG/

    另请观看以下视频:Communicating Between Controllers

    HTML:

    <div ng-controller="ControllerZero">
      <input ng-model="message" >
      <button ng-click="handleClick(message);">LOG</button>
    </div>
    
    <div ng-controller="ControllerOne">
      <input ng-model="message" >
    </div>
    
    <div ng-controller="ControllerTwo">
      <input ng-model="message" >
    </div>
    

    JavaScript的:

    var myModule = angular.module('myModule', []);
    myModule.factory('mySharedService', function($rootScope) {
      var sharedService = {};
    
      sharedService.message = '';
    
      sharedService.prepForBroadcast = function(msg) {
        this.message = msg;
        this.broadcastItem();
      };
    
      sharedService.broadcastItem = function() {
        $rootScope.$broadcast('handleBroadcast');
      };
    
      return sharedService;
    });
    
    function ControllerZero($scope, sharedService) {
      $scope.handleClick = function(msg) {
        sharedService.prepForBroadcast(msg);
      };
    
      $scope.$on('handleBroadcast', function() {
        $scope.message = sharedService.message;
      });        
    }
    
    function ControllerOne($scope, sharedService) {
      $scope.$on('handleBroadcast', function() {
        $scope.message = 'ONE: ' + sharedService.message;
      });        
    }
    
    function ControllerTwo($scope, sharedService) {
      $scope.$on('handleBroadcast', function() {
        $scope.message = 'TWO: ' + sharedService.message;
      });
    }
    
    ControllerZero.$inject = ['$scope', 'mySharedService'];        
    
    ControllerOne.$inject = ['$scope', 'mySharedService'];
    
    ControllerTwo.$inject = ['$scope', 'mySharedService'];
    
  • 688

    您可以在父控制器(MessageCtrl)中注入'$controller'服务,然后使用以下命令实例化/注入子控制器(DateCtrl):
    $scope.childController = $controller('childController', { $scope: $scope.$new() });

    现在,您可以通过调用其方法来访问子控制器中的数据,因为它是一项服务 .
    如果有任何问题,请告诉我 .

  • 3

    另外两个小提琴:(非服务方式)

    1)对于Parent-Child控制器 - 使用父控制器的 $scope 发出/广播事件 . http://jsfiddle.net/laan_sachin/jnj6y/

    2)在非相关控制器上使用 $rootScope . http://jsfiddle.net/VxafF/

  • 24

    如果要将一个控制器调用另一个控制器,则有四种方法可用

    • $ rootScope . $ emit()和$ rootScope . $ broadcast()

    • 如果第二个控制器是子控制器,则可以使用父子通信 .

    • 使用服务

    • 一种黑客攻击 - 在angular.element()的帮助下

    1. $ rootScope . $ emit()和$ rootScope . $ broadcast()

    控制器及其范围可能会被破坏,但$ rootScope仍然在整个应用程序中,这就是为什么我们要使用$ rootScope,因为$ rootScope是所有范围的父级 .

    如果您正在进行从父母到孩子的沟通,甚至孩子都想要与兄弟姐妹沟通,你可以使用$ broadcast

    如果您正在从孩子到父母进行通信,那么没有兄弟姐妹进行调用,那么您可以使用$ rootScope . $ emit

    HTML

    <body ng-app="myApp">
        <div ng-controller="ParentCtrl" class="ng-scope">
          // ParentCtrl
          <div ng-controller="Sibling1" class="ng-scope">
            // Sibling first controller
          </div>
          <div ng-controller="Sibling2" class="ng-scope">
            // Sibling Second controller
            <div ng-controller="Child" class="ng-scope">
              // Child controller
            </div>
          </div>
        </div>
    </body>
    

    Angularjs代码

    var app =  angular.module('myApp',[]);//We will use it throughout the example 
        app.controller('Child', function($rootScope) {
          $rootScope.$emit('childEmit', 'Child calling parent');
          $rootScope.$broadcast('siblingAndParent');
        });
    
    app.controller('Sibling1', function($rootScope) {
      $rootScope.$on('childEmit', function(event, data) {
        console.log(data + ' Inside Sibling one');
      });
      $rootScope.$on('siblingAndParent', function(event, data) {
        console.log('broadcast from child in parent');
      });
    });
    
    app.controller('Sibling2', function($rootScope) {
      $rootScope.$on('childEmit', function(event, data) {
        console.log(data + ' Inside Sibling two');
      });
      $rootScope.$on('siblingAndParent', function(event, data) {
        console.log('broadcast from child in parent');
      });
    });
    
    app.controller('ParentCtrl', function($rootScope) {
      $rootScope.$on('childEmit', function(event, data) {
        console.log(data + ' Inside parent controller');
      });
      $rootScope.$on('siblingAndParent', function(event, data) {
        console.log('broadcast from child in parent');
      });
    });
    

    在$ emit'childEmit'的上面代码控制台中不会调用子兄弟姐妹内部,它只调用父内部,其中$ broadcast也会在兄弟姐妹和父母内部调用 . 这是性能进入行动的地方 . $ emit是如果您使用子到父母的沟通,因为它会跳过一些脏检查,这是可取的 .

    2.如果第二个控制器是孩子,您可以使用Child Parent通信

    它是最好的方法之一,如果你想做 child parent communication ,孩子想要与 immediate parent 沟通,那么它不需要任何类型的$ broadcast或$ emit但是如果你想从父母到孩子进行沟通,那么你必须使用任何一种服务或$广播

    例如HTML: -

    <div ng-controller="ParentCtrl">
     <div ng-controller="ChildCtrl">
     </div>
    </div>
    

    Angularjs

    app.controller('ParentCtrl', function($scope) {
       $scope.value='Its parent';
          });
      app.controller('ChildCtrl', function($scope) {
       console.log($scope.value);
      });
    

    无论何时使用子到父进程通信,Angularjs都会在子进程内搜索变量,如果内部不存在变量,那么它将选择查看父控制器内的值 .

    3.使用服务

    AngularJS支持使用服务体系结构的"Seperation of Concerns"的概念 . 服务是javascript函数,只负责执行特定任务 . 这使得它们成为 individual entity ,这是用于使用Angularjs的依赖注入mecahnism注入的服务 .

    Angularjs代码:

    app.service('communicate',function(){
      this.communicateValue='Hello';
    });
    
    app.controller('ParentCtrl',function(communicate){//Dependency Injection
      console.log(communicate.communicateValue+" Parent World");
    });
    
    app.controller('ChildCtrl',function(communicate){//Dependency Injection
      console.log(communicate.communicateValue+" Child World");
    });
    

    它将输出Hello Child World和Hello Parent World . 根据Angular docs of services Singletons - 依赖于服务的每个组件都获得对服务工厂生成的单个实例的引用 .

    4.Kack of hack - 在angular.element()的帮助下

    这个方法从元素中获取scope()的Id / unique class.angular.element()方法返回元素,scope()给另一个变量的$ scope变量使用另一个控制器的$ scope变量,这不是一个好习惯 .

    HTML: -

    <div id='parent' ng-controller='ParentCtrl'>{{varParent}}
     <span ng-click='getValueFromChild()'>Click to get ValueFormChild</span>
     <div id='child' ng-controller='childCtrl'>{{varChild}}
       <span ng-click='getValueFromParent()'>Click to get ValueFormParent </span>
     </div>
    </div>
    

    Angularjs: -

    app.controller('ParentCtrl',function($scope){
     $scope.varParent="Hello Parent";
      $scope.getValueFromChild=function(){
      var childScope=angular.element('#child').scope();
      console.log(childScope.varChild);
      }
    });
    
    app.controller('ChildCtrl',function($scope){
     $scope.varChild="Hello Child";
      $scope.getValueFromParent=function(){
      var parentScope=angular.element('#parent').scope();
      console.log(parentScope.varParent);
      }
    });
    

    在上面的代码控制器在Html上显示它们自己的值,当你点击文本时,你将相应地在控制台中获得值 . 如果你点击父控制器 Span ,浏览器将控制子的值,反之亦然 .

  • 1

    If you are looking to emit & broadcast events to share data or call functions across controllers ,请查看link:并通过 zbynour 检查答案(以最大票数回答) . 我在引用他的答案!!!

    If scope of firstCtrl is parent of the secondCtrl scope, your code should work by replacing $emit by $broadcast in firstCtrl:

    function firstCtrl($scope){
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope){
        $scope.$on('someEvent', function(event, mass) {console.log(mass)});
    }
    

    In case there is no parent-child relation between your scopes you can inject $rootScope into the controller and broadcast the event to all child scopes (i.e. also secondCtrl).

    function firstCtrl($rootScope){
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
    

    Finally, when you need to dispatch the event from child controller to scopes upwards you can use $scope.$emit. If scope of firstCtrl is parent of the secondCtrl scope:

    function firstCtrl($scope){
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope){
        $scope.$emit('someEvent', [1,2,3]);
    }
    
  • 119

    如何在控制器之间进行通信有多种方法 .

    最好的可能是共享服务:

    function FirstController(someDataService) 
    {
      // use the data service, bind to template...
      // or call methods on someDataService to send a request to server
    }
    
    function SecondController(someDataService) 
    {
      // has a reference to the same instance of the service
      // so if the service updates state for example, this controller knows about it
    }
    

    另一种方法是在范围上发出事件:

    function FirstController($scope) 
    {
      $scope.$on('someEvent', function(event, args) {});
      // another controller or even directive
    }
    
    function SecondController($scope) 
    {
      $scope.$emit('someEvent', args);
    }
    

    在这两种情况下,您也可以与任何指令进行通信 .

  • 16

    我不知道这是否超出标准,但如果您将所有控制器放在同一个文件中,那么您可以执行以下操作:

    app = angular.module('dashboardBuzzAdmin', ['ngResource', 'ui.bootstrap']);
    
    var indicatorsCtrl;
    var perdiosCtrl;
    var finesCtrl;
    
    app.controller('IndicatorsCtrl', ['$scope', '$http', function ($scope, $http) {
      indicatorsCtrl = this;
      this.updateCharts = function () {
        finesCtrl.updateChart();
        periodsCtrl.updateChart();
      };
    }]);
    
    app.controller('periodsCtrl', ['$scope', '$http', function ($scope, $http) {
      periodsCtrl = this;
      this.updateChart = function() {...}
    }]);
    
    app.controller('FinesCtrl', ['$scope', '$http', function ($scope, $http) {
      finesCtrl = this;
      this.updateChart = function() {...}
    }]);
    

    正如您所看到的,在调用updateCharts时,indicatorsCtrl正在调用另一个控制器的updateChart函数 .

相关问题