Может ли один контроллер AngularJS вызвать другой? - PullRequest
572 голосов
/ 15 февраля 2012

Возможно ли, чтобы один контроллер использовал другой?

Например:

Этот документ 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, кажется, касается XmlHttpRequests и доступа к услугам.

Ответы [ 13 ]

700 голосов
/ 23 февраля 2012

Существует несколько способов связи между контроллерами.

Лучшим, вероятно, является сервис:

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

В обоих случаях вы также можете общаться с любой директивой.

121 голосов
/ 07 августа 2012

См. Эту скрипку: 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'];
52 голосов
/ 02 сентября 2012

Вот пример одной страницы двух контроллеров, совместно использующих служебные данные:

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

51 голосов
/ 17 июля 2015

Если вы хотите вызвать один контроллер в другой, доступны четыре метода

  1. $ rootScope. $ Emit () и $ rootScope. $ Broadcast ()
  2. Если Второй контроллер дочерний, вы можете использовать родительское дочернее общение.
  3. Услуги пользования
  4. Вид хака - с помощью 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. Если Второй контроллер дочерний, вы можете использовать связь Родитель-ребенок

Это один из лучших способов, если вы хотите дочернее родительское общение там, где ребенок хочет общаться с непосредственным родителем , тогда ему не понадобится никакой трансляции $ или $ 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 docs of services Singletons - каждый компонент, зависящий от сервиса, получает ссылку на один экземпляр, сгенерированный фабрикой сервисов .

4. Вид взлома - с помощью angular.element ()

Этот метод получает scope () из элемента по его идентификатору / уникальному методу class.angular.element () возвращает 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, и когда вы будете нажимать на текст, вы получите соответствующие значения в консоли. Если вы щелкнете по родительскому диапазону контроллеров, браузер отобразит значение дочернего элемента и наоборот.

32 голосов
/ 15 октября 2014

Если вы хотите отправлять и транслировать события для обмена данными или вызывать функции между контроллерами , просмотрите эту ссылку : и проверьте ответ по zbynour (ответьте с максимум голосов). Я цитирую его ответ !!!

Если область видимости firstCtrl является родительской областью видимости secondCtrl, ваш код должен работать, заменив $ emit на $ broadcast в firstCtrl:

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]);
}
24 голосов
/ 07 октября 2012

Еще две скрипки: (Необслуживаемый подход)

1) Для контроллера «Родитель-ребенок» - Использование $scope родительского контроллера для передачи / трансляции событий.http://jsfiddle.net/laan_sachin/jnj6y/

2) Использование $rootScope на несвязанных контроллерах.http://jsfiddle.net/VxafF/

16 голосов
/ 18 августа 2012

На самом деле использование излучения и широковещания неэффективно, потому что событие всплывает вверх и вниз по иерархии области видимости, что может легко перерасти в снижение производительности для сложного приложения.

Я бы предложил воспользоваться услугой. Вот как я недавно реализовал это в одном из моих проектов - https://gist.github.com/3384419.

Основная идея - зарегистрировать паб-саб / шину событий в качестве службы. Затем добавьте эту шину событий туда, где вам нужно подписаться или публиковать события / темы.

3 голосов
/ 20 октября 2014

Я не знаю, не соответствует ли это стандартам, но если все ваши контроллеры находятся в одном файле, вы можете сделать что-то вроде этого:

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() {...}
}]);

Как вы можете видеть,вызов функции updateChart двух других контроллеров при вызове updateCharts.

3 голосов
/ 16 июля 2013

Я тоже знаю об этом.

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

Но я не слишком часто его использую, потому что я не люблю использовать селекторы jQuery в угловом коде.

2 голосов
/ 16 апреля 2015

Вы можете внедрить службу '$ controller' в свой родительский контроллер (MessageCtrl), а затем создать / внедрить дочерний контроллер (DateCtrl), используя:
$scope.childController = $controller('childController', { $scope: $scope.$new() });

Теперь вы можете получить доступ к данным из вашегодочерний контроллер, вызывая его методы как сервис.
Дайте мне знать, если возникнут проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...