Czy jeden kontroler AngularJS może wywołać inny?

Czy jest możliwe, aby jeden kontroler używał innego?

Na przykład:

Ten dokument HTML po prostu drukuje wiadomość dostarczoną przez kontroler MessageCtrl w pliku 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>

Plik kontrolera zawiera następujący kod:

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

Który po prostu wyświetla aktualną datę;

Gdybym miał dodać inny kontroler, DateCtrl który przekazał datę w określonym formacie z powrotem do MessageCtrl, Jak można by to zrobić? Ramy DI wydają się być dotyczy XmlHttpRequests i dostępu do usług.

Author: Quinto, 2012-02-15

13 answers

Istnieje wiele sposobów komunikacji między kontrolerami.

Najlepsze to chyba dzielenie się serwisem:

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
}

Innym sposobem jest emitowanie zdarzenia na scope:

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

function SecondController($scope) 
{
  $scope.$emit('someEvent', args);
}

W obu przypadkach można również komunikować się z dowolną dyrektywą.

 684
Author: Vojta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-22 13:11:58

Zobacz to: http://jsfiddle.net/simpulton/XqDxG/

Obejrzyj także poniższy film: Komunikacja pomiędzy kontrolerami

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'];
 119
Author: adardesign,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-20 23:12:49

Oto jednostronicowy przykład dwóch kontrolerów dzielących dane usługi:

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

Również tutaj: https://gist.github.com/3595424

 52
Author: exclsr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 06:51:53

Jeśli chcesz wywołać jeden kontroler do drugiego, dostępne są cztery metody

  1. $rootScope.$emit () i $rootScope.$broadcast ()
  2. jeśli drugim kontrolerem jest dziecko, możesz użyć komunikacji rodzicielskiej.
  3. Skorzystaj Z Usług
  4. rodzaj hack - z pomocą angular.element ()

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

Kontroler i jego zakres mogą zostać zniszczone, ale $rootScope pozostaje w całej aplikacji, dlatego bierzemy $rootScope, ponieważ $rootScope jest rodzicem wszystkich zakresów .

Jeśli realizujesz komunikację z rodzicem do dziecka, a nawet dziecko chce komunikować się ze swoim rodzeństwem, możesz użyć $broadcast

Jeśli wykonujesz komunikację z dzieckiem do rodzica, nie ma rodzeństwa invovled, możesz użyć $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>

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

W powyższym kodzie konsoli $emit 'childEmit' będzie nie wywołaj wewnątrz rodzeństwa dziecka i wywoła wewnątrz tylko rodzica, gdzie $broadcast zostanie wywołany wewnątrz rodzeństwa i rodzica, jak również.To miejsce, w którym performance wchodzi w akcję.$emit jest preferowane, jeśli używasz komunikacji z dzieckiem do rodzica, ponieważ pomija niektóre brudne kontrole.

2. Jeśli drugim kontrolerem jest child, możesz użyć komunikacji Child Parent

Jest to jedna z najlepszych metod, jeśli chcesz zrobić dziecko rodzic komunikacja gdy dziecko chce komunikować się z bezpośrednim rodzicem wtedy nie będzie potrzebne żadne $broadcast lub $emit, ale jeśli chcesz komunikować się z rodzicem do dziecka, musisz użyć usługi lub $broadcast

Na przykład 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);
  });

Za każdym razem, gdy używasz komunikacji z dzieckiem do rodzica, Angularjs wyszukuje zmienną wewnątrz dziecka, jeśli nie jest obecna wewnątrz, wybierze, aby zobaczyć wartości wewnątrz kontrolera nadrzędnego.

3.Use Services

AngularJS wspiera koncepcje "oddzielenia obaw" za pomocą architektury usług. Usługi są funkcjami javascript i są odpowiedzialne tylko za wykonywanie określonych zadań.To sprawia, że są one indywidualną jednostką, która jest utrzymywalna i testowalna.Usługi używane do iniekcji za pomocą Dependency Injection mecahnism of Angularjs.

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

Będzie daj wyjście Hello Child World i Hello Parent World . Według Angular docs of services Singletons – każdy komponent zależny od usługi otrzymuje odniesienie do pojedynczej instancji generowanej przez service factory.

4.Rodzaj hack - z pomocą angular.element ()

Ta metoda pobiera scope () z elementu przez jego klasę Id / unique.kanciaste.metoda element () zwraca element i scope () daje zmienną $scope innej zmiennej używając $scope zmienna jednego kontrolera wewnątrz innego nie jest dobrą praktyką.

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

W powyższym kodzie Kontrolery wyświetlają swoją własną wartość w Html i po kliknięciu na tekst otrzymasz wartości w konsoli odpowiednio.Jeśli klikniesz na Kontrolery nadrzędne span, przeglądarka będzie Console wartość dziecka i viceversa.

 47
Author: Shubham Nigam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-25 13:36:43

Jeśli chcesz emitować i transmitować zdarzenia w celu udostępniania danych lub funkcji wywołania między kontrolerami, spójrz na ten link : i sprawdź odpowiedź przez zbynour (odpowiedź z maksymalnymi głosami). Cytuję jego odpowiedź !!!

Jeśli zakres firstCtrl jest rodzicem zakresu secondCtrl, Twój kod powinien działać poprzez zastąpienie $emit przez $broadcast w firstCtrl:

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

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

W przypadku, gdy nie ma relacji rodzic-dziecko między zakresami, możesz wprowadzić $rootScope do kontrolera i transmituje Zdarzenie do wszystkich zakresów potomnych (tj. również secondCtrl).

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

Na koniec, gdy trzeba wysłać Zdarzenie z kontrolera podrzędnego do zakresów w górę, można użyć $scope.$emit. Jeśli zakres firstCtrl jest rodzicem zakresu secondCtrl:

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

function secondCtrl($scope){
    $scope.$emit('someEvent', [1,2,3]);
}
 31
Author: SharpCoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:45

Jeszcze dwa skrzypce: (podejście bez usług)

1) dla kontrolera rodzic-dziecko-używanie $scope kontrolera rodzica do emitowania / transmitowania zdarzeń. http://jsfiddle.net/laan_sachin/jnj6y/

2) Używanie $rootScope przez kontrolery niezwiązane. http://jsfiddle.net/VxafF/

 24
Author: DarkKnight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-07 17:10:49

W rzeczywistości korzystanie z emit i broadcast jest nieefektywne, ponieważ zdarzenie pęka w górę iw dół hierarchii zakresu, co może łatwo obniżyć się do poziomu wydajności dla złożonej aplikacji.

Proponuję skorzystać z usługi. Oto jak ostatnio zaimplementowałem go w jednym z moich projektów - https://gist.github.com/3384419 .

Podstawowy pomysł-zarejestruj autobus pub-sub/event jako usługę. Następnie wstrzyknij tę magistralę zdarzeń, w której kiedykolwiek chcesz subskrybować lub opublikować wydarzenia/tematy.

 16
Author: numan salati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-04 16:53:19

Ja też znam ten sposób.

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

Ale nie używam go zbyt często, ponieważ nie lubię używać selektorów jQuery w kodzie kątowym.

 3
Author: Andrey Korchak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-04 21:25:54

Nie wiem, czy to jest poza standardami, ale jeśli masz wszystkie kontrolery w tym samym pliku, to możesz zrobić coś takiego:

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

Jak widać indicatorsCtrl wywołuje funkcje updateChart pozostałych kontrolerów podczas wywoływania funkcji updatechart.

 3
Author: tomascharad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-20 14:41:13

Istnieje metoda nie zależna od usług, $broadcast lub $emit. Nie jest to odpowiednie we wszystkich przypadkach, ale jeśli masz 2 powiązane kontrolery, które można wyodrębnić w dyrektywy, możesz użyć opcji require w definicji dyrektywy. Najprawdopodobniej tak komunikują się ngModel i ngForm. Można tego użyć do komunikacji pomiędzy kontrolerami dyrektywy, które są zagnieżdżone lub na tym samym elemencie.

Dla sytuacji rodzic / dziecko, użycie byłoby jak następuje:

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

I główne punkty, aby go uruchomić: na dyrektywie nadrzędnej, z metodami, które mają być wywołane, należy zdefiniować je na this (nie na $scope):

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

W definicji dyrektywy child można użyć opcji require, aby kontroler nadrzędny był przekazywany do funkcji link (można więc wywołać na nim funkcje z scope dyrektywy 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();
  }
}

Powyższe można zobaczyć na http://plnkr.co/edit/poeq460VmQER8Gl9w8Oz?p=preview

Dyrektywa rodzeństwa jest używana podobnie, ale obie dyrektywy dotyczące tego samego elementu:

<div directive1 directive2>
</div>

Używane przez tworzenie metody na directive1:

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

I w directive2 można to wywołać za pomocą opcji require, która powoduje przekazanie kontrolera siblingController do funkcji 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();
  }
}

Można to zobaczyć na http://plnkr.co/edit/MUD2snf9zvadfnDXq85w?p=preview .

Zastosowania tego?

  • Rodzic: każdy przypadek, w którym elementy potomne muszą się "zarejestrować" u rodzica. Podobnie jak związek między ngModel i ngForm. Mogą one dodawać pewne zachowania, które mogą wpływać na modele. Możesz mieć również coś opartego wyłącznie na DOM, gdzie element nadrzędny musi zarządzać pozycjami niektórych dzieci, np. zarządzać lub reagować na przewijanie.

  • Możliwość modyfikacji zachowania dyrektywy. ngModel jest klasyczny przypadek, aby dodać parsery / walidację do ngModel użyj na wejściach.

 2
Author: Michal Charemza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-27 13:16:15

Możesz wprowadzić usługę '$ controller' do kontrolera nadrzędnego (MessageCtrl), a następnie utworzyć instancję / wstrzyknąć kontroler podrzędny (DateCtrl) za pomocą:
$scope.childController = $controller('childController', { $scope: $scope.$new() });

Teraz możesz uzyskać dostęp do danych z kontrolera dziecka, wywołując jego metody, ponieważ jest to usługa.
Daj mi znać, jeśli jakiś problem.

 2
Author: Smrutiranjan Sahu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-16 18:56:12

Poniżej znajduje się podejście publish-subscribe, które jest niezależne od kątowego 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;
 1
Author: Lijo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-21 14:10:28

W angular 1.5 można to osiągnąć wykonując następujące czynności:

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

}());

Jest to komponent nadrzędny. W tym stworzyłem funkcję, która wypycha inny obiekt do mojej tablicy productForms - notatka - to tylko mój przykład, ta funkcja może być naprawdę wszystkim.

Teraz możemy utworzyć kolejny komponent, który będzie używał 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;
    };  
  }

}());

Tutaj komponent potomny tworzy odniesienie do funkcji komponentu parents addNewForm, która może być następnie powiązana z HTML i wywoływana jak każda inna funkcja.

 1
Author: Katana24,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-23 17:10:13