Может ли один контроллер AngularJS вызвать другой?

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

например:

этот HTML-документ просто печатает сообщение доставлено .

<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 ответов


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

лучший-это, наверное, общий доступ к сервису:

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

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


см. эту скрипку: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'];

вот одностраничный пример двух контроллеров обмена данными службы:

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


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

  1. $rootScope.$emit () и $rootScope.$ broadcast ()
  2. если второй контроллер является дочерним, вы можете использовать родительскую дочернюю связь .
  3. Использовать Услуги
  4. вид взлома-с помощью angular.element ()

1. метрики.$emit () и метрики.$ 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. Если Второй контроллер является дочерним, вы можете использовать дочернюю родительскую связь

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

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

этот метод получает scope () от элемента по его Id / уникальному классу.угловатый.метод 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, браузер будет консольное значение дочернего и viceversa.


если вы хотите излучать и транслировать события для обмена данными или вызова функций между контроллерами, пожалуйста, посмотрите на это ссылке: и проверьте ответ на 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.$излучать. Если область firstCtrl является родительской для области secondCtrl:

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

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

еще две скрипки: (не сервисный подход)

1) для родительско-дочернего контроллера-с помощью $scope родительского контроллера для излучения / трансляции событий. http://jsfiddle.net/laan_sachin/jnj6y/

2) с помощью $rootScope через не связанные контроллеры. http://jsfiddle.net/VxafF/


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

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

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


Я также знаю об этом пути.

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

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


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

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

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


существует метод, не зависящий от услуг,$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 опция, поэтому родительский контроллер передается функции link (так что вы можете вызвать функции на нем из scope директивы child.

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

аналогично используется директива sibling, но обе директивы для одного элемента:

<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: любой случай, когда дочерние элементы должны "зарегистрироваться" у родителя. Очень похоже на отношения между ngModel и ngForm. Они могут добавить определенное поведение, которое может повлиять на модели. У вас также может быть что-то чисто DOM, где родительский элемент должен управлять позициями определенных детей, скажем, управлять или реагировать на прокрутка.

  • Sibling: позволяет директиве изменять свое поведение. ngModel-это классический случай, чтобы добавить Парсеры / проверку к использованию ngModel на входах.


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

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


ниже publish-subscribe подход, который независимо от углового JS.

Поиск Контроллер Param

//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 array-note - это только мой пример, эта функция может быть чем угодно.

теперь мы можем создать другой компонент, который будет использовать 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 и вызвать как любую другую функцию.