web-dev-qa-db-fra.com

Un contrôleur AngularJS peut-il en appeler un autre?

Est-il possible qu'un contrôleur en utilise un autre?

Par exemple:

Ce document HTML imprime simplement un message envoyé par le contrôleur MessageCtrl dans le fichier 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>

Le fichier du contrôleur contient le code suivant:

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

Qui imprime simplement la date actuelle;

Si je devais ajouter un autre contrôleur, DateCtrl qui a remis la date dans un format spécifique à MessageCtrl, comment procéder pour cela? Le cadre des ID semble être concerné par XmlHttpRequests et par l’accès aux services.

572
BanksySan

Il existe plusieurs façons de communiquer entre contrôleurs.

Le meilleur est probablement le partage d'un service:

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
}

Une autre façon est d'émettre un événement sur la portée:

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

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

Dans les deux cas, vous pouvez également communiquer avec n’importe quelle directive.

700
Vojta

Voir ce violon: http://jsfiddle.net/simpulton/XqDxG/

Regardez aussi la vidéo suivante: Communication entre contrôleurs

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

Voici un exemple d'une page de deux contrôleurs partageant des données de service:

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

Aussi ici: https://Gist.github.com/3595424

52
exclsr

Si vous souhaitez appeler un contrôleur sur un autre, quatre méthodes sont disponibles.

  1. $ rootScope. $ emit () et $ rootScope. $ broadcast ()
  2. Si le second contrôleur est un enfant, vous pouvez utiliser la communication parent-enfant.
  3. Utiliser les services
  4. Type de bidouille - avec l'aide de angular.element ()

1. $ RootScope. $ Emit () et $ rootScope. $ Broadcast ()

Le contrôleur et son étendue peuvent être détruits, mais le $ rootScope reste inchangé dans l’application, c’est pourquoi nous prenons $ rootScope car $ rootScope est le parent de toutes les étendues.

Si vous établissez une communication de parent à enfant et que même un enfant souhaite communiquer avec ses frères et soeurs, vous pouvez utiliser $ broadcast

Si vous établissez une communication d’enfant à parent, aucun frère ou sœur n’ayant été envahi, vous pouvez utiliser $ 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>

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

Dans le code ci-dessus, la console de $ emit 'childEmit' n'appellera pas à l'intérieur de la fratrie enfant et n'appellera qu'à l'intérieur du parent, où $ broadcast sera appelé à l'intérieur de la fratrie et du parent également. C'est l'endroit où la performance entre en action. préférable, si vous utilisez une communication enfant à parent car elle évite certaines vérifications imprécises.

2. Si le second contrôleur est un enfant, vous pouvez utiliser la communication enfant parent

C’est l’une des meilleures méthodes, si vous voulez faire communication parent-enfant où l’enfant veut communiquer avec parent immédiat alors il n’aurait pas besoin de quelque sorte que ce soit $ broadcast ou $ emit mais si vous voulez faire la communication de parent à enfant, alors vous devez utiliser soit le service, soit $ broadcast

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

Chaque fois que vous utilisez une communication enfant à parent, Angularjs recherchera une variable à l'intérieur de l'enfant. Si elle n'est pas présente à l'intérieur, elle choisira de voir les valeurs à l'intérieur du contrôleur parent.

. Services d'utilisation

AngularJS supporte les concepts de "Séparation des problèmes" en utilisant une architecture de services. Les services sont des fonctions javascript et sont responsables de l'exécution de tâches spécifiques uniquement. Cela les rend une entité individuelle qui est maintenable et testable. Les services utilisés pour l'injection à l'aide du mécatisme d'Angularjs par injection de dépendance .

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

Il donnera les sorties Hello Child World et Hello Parent World. Selon Angular docs de services Singletons - Chaque composant dépendant d'un service obtient une référence à l'instance unique générée par la fabrique de services .

4. Genre de bidouille - avec l'aide de angular.element ()

Cette méthode obtient scope () de l'élément par son ID/unique class.angular.element () renvoie element et scope () donne la variable $ scope d'une autre variable en utilisant la variable $ scope d'un contrôleur à l'intérieur d'un autre, ce n'est pas une bonne pratique.

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

Dans ce qui précède, les contrôleurs de code affichent leur propre valeur en HTML et, lorsque vous cliquerez sur un texte, vous obtiendrez les valeurs correspondantes dans la console.

51
Shubham Nigam

Si vous souhaitez émettre et diffuser des événements pour partager des données ou appeler des fonctions entre contrôleurs , veuillez regarder ceci lien : et vérifiez la réponse par zbynour (réponse avec un maximum de voix). Je cite sa réponse !!!

Si la portée de firstCtrl est parent de la portée de secondCtrl, votre code devrait fonctionner en remplaçant $ emit par $ broadcast dans firstCtrl:

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

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

Au cas où il n'y aurait pas de relation parent-enfant entre vos portées, vous pouvez injecter $ rootScope dans le contrôleur et diffuser l'événement à tous les portées enfants (c'est-à-dire également secondCtrl).

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

Enfin, lorsque vous devez envoyer l'événement du contrôleur enfant aux portées, vous pouvez utiliser $ scope. $ Emit. Si la portée de firstCtrl est parent de la portée de secondCtrl:

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

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

Deux autres violons: (approche sans service)

1) Pour contrôleur parent-enfant - Utiliser $scope du contrôleur parent pour émettre/diffuser des événements. http://jsfiddle.net/laan_sachin/jnj6y/

2) Utilisation de $rootScope sur des contrôleurs non liés. http://jsfiddle.net/VxafF/

24
DarkKnight

En fait, l’utilisation d’émettre et de diffuser est inefficace car l’événement bouillonne dans la hiérarchie de l’étendue, ce qui peut facilement dégrader en performances pour une application complexe.

Je suggère d'utiliser un service. Voici comment je l'ai récemment implémenté dans l'un de mes projets - https://Gist.github.com/3384419 .

Idée de base - enregistrez un bus pub-sub/event en tant que service. Ensuite, injectez ce bus d’événements là où vous avez besoin de vous abonner ou de publier des événements/sujets.

16
numan salati

Je ne sais pas si c'est hors normes, mais si vous avez tous vos contrôleurs sur le même fichier, vous pouvez faire quelque chose comme ça:

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

Comme vous pouvez le voir, indicateursCtrl appelle les fonctions updateChart des deux autres contrôleurs lors de l’appel updateCharts.

3
tomascharad

Je sais aussi de cette façon.

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

Mais je ne l'utilise pas trop, car je n'aime pas utiliser les sélecteurs jQuery dans le code angular.

3
Andrey Korchak

Il existe une méthode qui ne dépend pas des services, $broadcast ou $emit. Cela ne convient pas dans tous les cas, mais si vous avez 2 contrôleurs liés qui peuvent être résumés en directives, vous pouvez utiliser l'option require dans la définition de la directive. C’est très probablement comment ngModel et ngForm communiquent. Vous pouvez l'utiliser pour communiquer entre des contrôleurs de directive imbriqués ou sur le même élément.

Pour une situation parent/enfant, l'utilisation serait la suivante:

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

Et les points principaux pour le faire fonctionner: Sur la directive parent, avec les méthodes à appeler, vous devez les définir sur this (pas sur $scope):

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

Dans la définition de la directive enfant, vous pouvez utiliser l'option require pour que le contrôleur parent soit transmis à la fonction link (vous pouvez donc appeler des fonctions à partir de la variable scope de la directive enfant.

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

Ce qui précède peut être vu à l’adresse http://plnkr.co/edit/poeq460VmQER8Gl9w8Oz?p=preview

Une directive similaire est utilisée de la même manière, mais les deux directives concernent le même élément:

<div directive1 directive2>
</div>

Utilisé en créant une méthode sur directive1:

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

Et dans directive2, cela peut être appelé à l'aide de l'option require, ce qui a pour effet de transmettre le siblingController à la fonction 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();
  }
}

Cela peut être vu à http://plnkr.co/edit/MUD2snf9zvadfnDXq85w?p=preview .

Les utilisations de cela?

  • Parent: Dans tous les cas où des éléments enfants doivent "s'inscrire" auprès d'un parent. Tout comme la relation entre ngModel et ngForm. Ceux-ci peuvent ajouter certains comportements pouvant affecter les modèles. Vous pouvez également avoir quelque chose de purement basé sur le DOM, dans lequel un élément parent doit gérer les positions de certains enfants, par exemple, pour gérer le défilement ou réagir à celui-ci.

  • Frère/soeur: permet à une directive de modifier son comportement. ngModel est le cas classique, pour ajouter des analyseurs/validation à l'utilisation de ngModel sur les entrées.

2
Michal Charemza

Vous pouvez injecter le service '$ controller' dans votre contrôleur parent (MessageCtrl), puis instancier/injecter le contrôleur enfant (DateCtrl) en utilisant:
$scope.childController = $controller('childController', { $scope: $scope.$new() });

Vous pouvez maintenant accéder aux données de votre contrôleur enfant en appelant ses méthodes car il s'agit d'un service.
Faites-moi savoir s'il y a un problème.

2
Smrutiranjan Sahu

Voici une approche de publish-subscribe indépendante de Angular JS.

Contrôleur de paramètres de recherche

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

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

Contrôleur de choix de recherche

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


});

gestionnaire d'événements

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
Lijo

Dans angular 1.5, ceci peut être accompli en procédant comme suit:

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

}());

Ceci est le composant parent. En cela, j'ai créé une fonction qui pousse un autre objet dans mon tableau productForms - remarque - ceci est juste mon exemple, cette fonction peut être quelque chose de vraiment.

Nous pouvons maintenant créer un autre composant qui utilisera 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;
    };  
  }

}());

Ici, le composant enfant crée une référence à la fonction du composant parent addNewForm qui peut ensuite être liée au code HTML et appelée comme n'importe quelle autre fonction.

1
Katana24