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

IT技术 javascript html angularjs
2021-02-02 08:18:07

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

例如:

这个 HTML 文档只是MessageCtrlmessageCtrl.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 框架似乎关注XmlHttpRequests和访问服务。

6个回答

控制器之间有多种通信方式。

最好的可能是共享服务:

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

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

您好,第一个示例需要网页了解堆栈中的所有服务。感觉像难闻的气味(?)。与第二个一样,网页不需要提供 $scope 参数吗?
2021-03-15 08:18:07
@JoshNoe in 1/ 你有两个(或更多)控制器,它们都得到一个相同/共享的服务。然后,您有多种沟通方式,您提到了其中一些。我会根据您的特定用例来决定。您可以将共享逻辑/状态放入服务中,并且两个控制器仅委托给该服务,甚至将服务导出到模板。当然,该服务也可以触发事件......
2021-03-15 08:18:07
这么晚了:你们确实知道你正在和谷歌的 The Vojta 争论,他在 AngularJS 上工作,对吧?:)
2021-03-22 08:18:07
什么?为什么?所有控制器都由 Angular 的 DI 注入。
2021-03-27 08:18:07
在我的 HTML 中,事件发射控制器必须是监听控制器的子节点,这对我来说并不明显。
2021-03-30 08:18:07

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

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

网址:

<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'];
优秀。不过,我有一个问题:为什么要在 ControllerZero 中添加处理程序?$scope.$on('handleBroadcast', function() { $scope.message = sharedService.message; });
2021-03-19 08:18:07
@adardesign:我很想阅读同样简洁而有意义的指令示例(也感谢您的回答!)
2021-03-21 08:18:07
上面的小提琴和视频共享一个服务。这是一个使用 $scope.$emit 的小提琴:jsfiddle.net/VxafF
2021-04-04 08:18:07
很好的答案,我使用 myModule.service('mySharedService', function($rootScope) {}) 而不是 myModule.factory 但它仍然有效!
2021-04-08 08:18:07
提供的视频真的很棒!我似乎这就是我需要从另一个控制器查询另一个控制器的状态。但是,这不适用于“调用”功能。它使用“触发”动作工作。如此有效地,如果控制器执行了一个动作,并有一个新的状态,那么它就必须广播状态,其他控制器来监听广播并做出相应的响应。或者更好的是,在共享服务中执行操作,然后广播状态。请告诉我我的理解是否正确。
2021-04-09 08:18:07

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

  1. $rootScope.$emit() 和 $rootScope.$broadcast()
  2. 如果第二个控制器是子控制器,则可以使用父子通信。
  3. 使用服务
  4. 一种 hack - 在 angular.element() 的帮助下

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

控制器及其作用域可能会被破坏,但 $rootScope 仍然存在于整个应用程序中,这就是我们采用 $rootScope 的原因,因为 $rootScope 是所有作用域的父级。

如果你正在执行从父母到孩子的交流,甚至孩子也想和它的兄弟姐妹交流,你可以使用 $broadcast

如果您正在执行从孩子到父母的通信,没有兄弟姐妹 invovled 那么您可以使用 $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.如果Second controller是child,可以使用Child Parent通信

它是最好的方法之一,如果你想在孩子想要与直接父母沟通的情况进行孩子父母的沟通那么它不需要任何类型的 $broadcast 或 $emit 但如果你想从父母到孩子进行沟通,那么你必须使用服务或 $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 文档- 依赖于服务的每个组件都获得对服务工厂生成的单个实例的引用

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

此方法通过元素的 Id / 唯一 class.angular.element() 方法从元素获取 scope() 方法返回元素,并且 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 上显示自己的值,当您单击文本时,您将在控制台中获得相应的值。如果您单击父控制器跨度,浏览器将控制子控制器的值,反之亦然。

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

<!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

如果theService更新thing.x,那么这个变化自动propageates到<input> S INFirstCtrlSecondCtrl,对不对?thing.x可以通过两个 <input> 中的任何一个直接更改(对吗?)。
2021-03-10 08:18:07
到目前为止,我在网上看到的最好的例子。谢谢
2021-03-22 08:18:07
我之前评论中的链接是 404,所以这里是服务指南,今天,注意服务是单例:docs.angularjs.org/guide/services
2021-03-27 08:18:07
@exclsr 是的!抱歉我之前错过了
2021-03-30 08:18:07
是的。所有 Angular 服务都是应用程序单例,这意味着只有一个服务实例。参考:docs.angularjs.org/guide/dev_guide.services.creating_services
2021-04-03 08:18:07

如果您希望发出和广播事件以在控制器之间共享数据或调用函数,请查看此链接:并通过zbynour(answer with max votes)检查答案我引用他的回答!!!

如果 firstCtrl 的作用域是 secondCtrl 作用域的父级,您的代码应该通过在 firstCtrl 中用 $broadcast 替换 $emit 来工作:

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

function secondCtrl($scope){
    $scope.$on('someEvent', function(event, mass) {console.log(mass)});
}

如果您的范围之间没有父子关系,您可以将 $rootScope 注入控制器并将事件广播到所有子范围(即也是 secondCtrl)。

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

最后,当您需要将事件从子控制器分派到范围向上时,您可以使用 $scope.$emit。如果 firstCtrl 的作用域是 secondCtrl 作用域的父级:

function firstCtrl($scope){
    $scope.$on('someEvent', function(event, data) { console.log(data); });
}

function secondCtrl($scope){
    $scope.$emit('someEvent', [1,2,3]);
}