Comment injecter un contrôleur dans un autre contrôleur dans AngularJS

97

Je suis nouveau sur Angular et j'essaie de comprendre comment faire les choses ...

En utilisant AngularJS, comment puis-je injecter un contrôleur à utiliser dans un autre contrôleur?

J'ai l'extrait suivant:

var app = angular.module("testApp", ['']);

app.controller('TestCtrl1', ['$scope', function ($scope) {
    $scope.myMethod = function () {
        console.log("TestCtrl1 - myMethod");
    }
}]);

app.controller('TestCtrl2', ['$scope', 'TestCtrl1', function ($scope, TestCtrl1) {
    TestCtrl1.myMethod();
}]);

Lorsque j'exécute cela, j'obtiens l'erreur:

Error: [$injector:unpr] Unknown provider: TestCtrl1Provider <- TestCtrl1
http://errors.angularjs.org/1.2.21/$injector/unpr?p0=TestCtrl1Provider%20%3C-%20TestCtrl1

Dois-je même essayer d'utiliser un contrôleur à l'intérieur d'un autre contrôleur, ou devrais-je en faire un service?

Scottie
la source
2
Vous ne pouvez pas injecter des contrôleurs les uns dans les autres. Oui, vous devriez TestCtrl1plutôt vous transformer en service.
Sly_cardinal
Exactement, utilisez les services
Miguel Mota
3
et si je devais mettre à jour une propriété d'un contrôleur qui se lie à la vue. Cette propriété est affectée par l'événement qui se produit dans un autre contrôleur.
Ankit Tanna

Réponses:

129

Si votre intention est de vous procurer un contrôleur déjà instancié d'un autre composant et que si vous suivez une approche basée sur un composant / une directive, vous pouvez toujours requireun contrôleur (instance d'un composant) d'un autre composant qui suit une certaine hiérarchie.

Par exemple:

//some container component that provides a wizard and transcludes the page components displayed in a wizard
myModule.component('wizardContainer', {
  ...,
  controller : function WizardController() {
    this.disableNext = function() { 
      //disable next step... some implementation to disable the next button hosted by the wizard
    }
  },
  ...
});

//some child component
myModule.component('onboardingStep', {
 ...,
 controller : function OnboadingStepController(){

    this.$onInit = function() {
      //.... you can access this.container.disableNext() function
    }

    this.onChange = function(val) {
      //..say some value has been changed and it is not valid i do not want wizard to enable next button so i call container's disable method i.e
      if(notIsValid(val)){
        this.container.disableNext();
      }
    }
 },
 ...,
 require : {
    container: '^^wizardContainer' //Require a wizard component's controller which exist in its parent hierarchy.
 },
 ...
});

Maintenant, l'utilisation de ces composants ci-dessus pourrait être quelque chose comme ceci:

<wizard-container ....>
<!--some stuff-->
...
<!-- some where there is this page that displays initial step via child component -->

<on-boarding-step ...>
 <!--- some stuff-->
</on-boarding-step>
...
<!--some stuff-->
</wizard-container>

Il existe de nombreuses façons de configurer require .

(pas de préfixe) - Localisez le contrôleur requis sur l'élément actuel. Lancer une erreur si non trouvée.

? - Essayez de localiser le contrôleur requis ou passez null au lien fn s'il n'est pas trouvé.

^ - Localisez le contrôleur requis en recherchant l'élément et ses parents. Lancer une erreur si non trouvée.

^^ - Localisez le contrôleur requis en recherchant les parents de l'élément. Lancer une erreur si non trouvée.

? ^ - Essayez de localiser le contrôleur requis en recherchant l'élément et ses parents ou passez null au lien fn s'il n'est pas trouvé.

? ^^ - Essayez de localiser le contrôleur requis en recherchant les parents de l'élément, ou passez null au lien fn s'il n'est pas trouvé.



Ancienne réponse:

Vous devez injecter un $controllerservice pour instancier un contrôleur dans un autre contrôleur. Mais sachez que cela peut entraîner des problèmes de conception. Vous pouvez toujours créer des services réutilisables qui suivent une responsabilité unique et les injecter dans les contrôleurs selon vos besoins.

Exemple:

app.controller('TestCtrl2', ['$scope', '$controller', function ($scope, $controller) {
   var testCtrl1ViewModel = $scope.$new(); //You need to supply a scope while instantiating.
   //Provide the scope, you can also do $scope.$new(true) in order to create an isolated scope.
   //In this case it is the child scope of this scope.
   $controller('TestCtrl1',{$scope : testCtrl1ViewModel });
   testCtrl1ViewModel.myMethod(); //And call the method on the newScope.
}]);

Dans tous les cas, vous ne pouvez pas appeler TestCtrl1.myMethod() car vous avez attaché la méthode sur $scopeet non sur l'instance de contrôleur.

Si vous partagez le contrôleur, il serait toujours préférable de faire: -

.controller('TestCtrl1', ['$log', function ($log) {
    this.myMethod = function () {
        $log.debug("TestCtrl1 - myMethod");
    }
}]);

et en consommant faire:

.controller('TestCtrl2', ['$scope', '$controller', function ($scope, $controller) {
     var testCtrl1ViewModel = $controller('TestCtrl1');
     testCtrl1ViewModel.myMethod();
}]);

Dans le premier cas, c'est vraiment $scopevotre modèle de vue, et dans le second cas, c'est l'instance de contrôleur elle-même.

PSL
la source
4
Et cela dépend de la fonctionnalité fournie par le contrôleur.Si vous en faites plus un modèle de vue que vous devez partager entre les composants, c'est bien, mais s'il s'agit davantage d'une fonctionnalité de fournisseur de services, je voudrais simplement créer un service. .
PSL
Devrait var testCtrl1ViewModel = $scope.$new();être var testCtrl1ViewModel = $rootScope.$new();? se référer à: docs.angularjs.org/guide/controller @PSL
leonsPAPA
Dans l'exemple ci-dessus, vous accédez au conteneur sur le contrôleur de directive, mais je ne peux pas faire fonctionner cela. Je peux accéder aux contrôleurs requis via le quatrième paramètre de ma fonction de liaison sur la directive elle-même. Mais ils ne sont pas liés au contrôleur de directive comme dans l'exemple ci-dessus. Quelqu'un d'autre a-t-il ce problème?
Sammi
33

Je suggère que la question que vous devriez vous poser est de savoir comment injecter des services dans les contrôleurs. Les gros services avec des contrôleurs maigres sont une bonne règle de base, c'est-à-dire utilisez simplement des contrôleurs pour coller votre service / usine (avec la logique métier) dans vos vues.

Les contrôleurs obtiennent des ordures collectées lors des modifications d'itinéraire.Par exemple, si vous utilisez des contrôleurs pour conserver la logique métier qui restitue une valeur, vous perdrez l'état sur deux pages si l'utilisateur de l'application clique sur le bouton Précédent du navigateur.

var app = angular.module("testApp", ['']);

app.factory('methodFactory', function () {
    return { myMethod: function () {
            console.log("methodFactory - myMethod");
    };
};

app.controller('TestCtrl1', ['$scope', 'methodFactory', function ($scope,methodFactory) {  //Comma was missing here.Now it is corrected.
    $scope.mymethod1 = methodFactory.myMethod();
}]);

app.controller('TestCtrl2', ['$scope', 'methodFactory', function ($scope, methodFactory) {
    $scope.mymethod2 = methodFactory.myMethod();
}]);

Voici une démonstration de fonctionnement de l'usine injectée dans deux contrôleurs

Aussi, je suggérerais de lire ce tutoriel sur les services / usines.

bâtard effronté
la source
13

Il n'est pas nécessaire d'importer / d'injecter votre contrôleur dans JS. Vous pouvez simplement injecter votre contrôleur / contrôleur imbriqué via votre HTML, cela a fonctionné pour moi. Comme :

<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>
chetanpawar
la source
2
vrai ... mais je pense toujours qu'il vaut mieux mettre tous les éléments communs dans un service et injecter le service au contrôleur respectif.
Neel
-1
<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

Cela fonctionne mieux dans mon cas, où TestCtrl2 a ses propres directives.

var testCtrl2 = $controller('TestCtrl2')

Cela me donne une erreur indiquant une erreur d'injection scopeProvider.

   var testCtrl1ViewModel = $scope.$new();
   $controller('TestCtrl1',{$scope : testCtrl1ViewModel });
   testCtrl1ViewModel.myMethod(); 

Cela ne fonctionne pas vraiment si vous avez des directives dans 'TestCtrl1', cette directive a en fait une portée différente de celle créée ici. Vous vous retrouvez avec deux instances de «TestCtrl1».

binRAIN
la source
-1

La meilleure solution:-

angular.module("myapp").controller("frstCtrl",function($scope){$scope.name="Atul Singh";}).controller("secondCtrl",function($scope){angular.extend(this, $controller('frstCtrl', {$scope:$scope}));console.log($scope);})

// Ici, vous avez le premier appel de contrôleur sans l'exécuter

Atul Singh
la source
-1

vous pouvez également utiliser $rootScopepour appeler une fonction / méthode du 1er contrôleur à partir du deuxième contrôleur comme celui-ci,

.controller('ctrl1', function($rootScope, $scope) {
     $rootScope.methodOf2ndCtrl();
     //Your code here. 
})

.controller('ctrl2', function($rootScope, $scope) {
     $rootScope.methodOf2ndCtrl = function() {
     //Your code here. 
}
})
user5943763
la source
1
Downvote: Il s'agit simplement d'un mauvais codage: vous rendez simplement votre fonction globale. Mieux vaut supprimer complètement Angular si c'est ainsi que vous voulez coder ... Utilisez un service comme suggéré par la plupart des autres réponses.
HammerNL
Ceci n'est pas recommandé. $ rootScope rend le code maladroit et conduit à des problèmes à long terme.
Pantalon Harshit
-2

utilisez du dactylographie pour votre codage, car il est orienté objet, strictement typé et facile à maintenir le code ...

pour plus d'informations sur typescipt cliquez ici

Voici un exemple simple que j'ai créé pour partager des données entre deux contrôleurs en utilisant Typescript ...

module Demo {
//create only one module for single Applicaiton
angular.module('app', []);
//Create a searvie to share the data
export class CommonService {
    sharedData: any;
    constructor() {
        this.sharedData = "send this data to Controller";
    }
}
//add Service to module app
angular.module('app').service('CommonService', CommonService);

//Create One controller for one purpose
export class FirstController {
    dataInCtrl1: any;
    //Don't forget to inject service to access data from service
    static $inject = ['CommonService']
    constructor(private commonService: CommonService) { }
    public getDataFromService() {
        this.dataInCtrl1 = this.commonService.sharedData;
    }
}
//add controller to module app
angular.module('app').controller('FirstController', FirstController);
export class SecondController {
    dataInCtrl2: any;
    static $inject = ['CommonService']
    constructor(private commonService: CommonService) { }
    public getDataFromService() {
        this.dataInCtrl2 = this.commonService.sharedData;
    }
}
angular.module('app').controller('SecondController', SecondController);

}

UniCoder
la source