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

例如:

这个HTML文档只是打印MessageCtrl控制器在MessageCtrl .js文件中传递的消息。

<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框架似乎与xmlhttprequest和访问服务有关。


当前回答

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

请观看以下视频:控制器之间的通信

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'];

其他回答

下面是一个与Angular JS无关的发布-订阅方法。

搜索参数控制器

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

plantButtonClicked: function () 
{
        EM.fireEvent('onSearchParamSelectedEvent', 'plant');
},

搜索选择控制器

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


});

事件管理器

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

全球

var EM = myBase.EventManager;

在angular 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并像其他函数一样调用。

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

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

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

在父指令中,使用要调用的方法,你应该在this(而不是$scope)上定义它们:

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

在子指令定义上,你可以使用require选项,这样父控制器就被传递给了链接函数(这样你就可以从子指令的作用域调用它上的函数)。

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被传递给link函数:

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: Any case where child elements need to "register" themselves with a parent. Much like the relationship between ngModel and ngForm. These can add certain behaviour that can affects models. You might have something purely DOM based as well, where a parent element needs to manage the positions of certain children, say to manage or react to scrolling. Sibling: allowing a directive to have its behaviour modified. ngModel is the classic case, to add parsers / validation to ngModel use on inputs.

如果你想将一个控制器调用到另一个控制器中,有四种方法可用

$rootScope.$emit()和$rootScope.$broadcast() 如果第二控制器是子控制器,你可以使用父-子通信。 使用服务 有点hack -在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. 如果第二控制器是子控制器,你可以使用子-父通信

这是最好的方法之一,如果你想做孩子父母通信,孩子想与直接的父母通信,那么它不需要任何类型的$broadcast或$emit,但如果你想做从父母到孩子的通信,那么你必须使用service或$broadcast

例如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使用服务架构支持“关注点分离”的概念。服务是javascript函数,只负责执行特定的任务。这使它们成为可维护和可测试的独立实体。使用Angularjs的依赖注入机制进行注入的服务。

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的服务文档,singleton——每个依赖于服务的组件都会获得一个由服务工厂生成的单个实例的引用。

4.有点hack -在angular.element()的帮助下

这个方法通过它的Id / unique class.angular.element()方法从元素中获取scope()返回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和当你将点击文本,你会得到相应的值在控制台。如果你点击父控制器跨度,浏览器将控制台的child值,反之亦然。

我也知道这条路。

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

但我不太使用它,因为我不喜欢在angular代码中使用jQuery选择器。