Wie injiziere ich einen Controller in einen anderen Controller in AngularJS?

97

Ich bin neu in Angular und versuche herauszufinden, wie man Dinge macht ...

Wie kann ich mit AngularJS einen Controller injizieren, der in einem anderen Controller verwendet werden soll?

Ich habe das folgende Snippet:

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

Wenn ich dies ausführe, erhalte ich die Fehlermeldung:

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

Sollte ich überhaupt versuchen, einen Controller in einem anderen Controller zu verwenden, oder sollte ich dies zu einem Dienst machen?

Scottie
quelle
2
Sie können keine Controller ineinander injizieren. Ja, Sie sollten TestCtrl1stattdessen in einen Dienst wechseln .
Sly_cardinal
Genau, nutzen Sie Dienste
Miguel Mota
3
Was wäre, wenn ich eine Eigenschaft eines Controllers aktualisieren müsste, der an die Ansicht gebunden ist? Diese Eigenschaft wird durch das Ereignis in einem anderen Controller beeinflusst.
Ankit Tanna

Antworten:

129

Wenn Sie beabsichtigen, einen bereits instanziierten Controller einer anderen Komponente zu erhalten, und wenn Sie einen komponenten- / richtlinienbasierten Ansatz verfolgen, können Sie immer requireeinen Controller (eine Instanz einer Komponente) von einer anderen Komponente aus verwenden, die einer bestimmten Hierarchie folgt.

Beispielsweise:

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

Die Verwendung dieser oben genannten Komponenten könnte nun ungefähr so ​​aussehen:

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

Es gibt viele Möglichkeiten , wie Sie festlegen können bis erfordern .

(kein Präfix) - Suchen Sie den erforderlichen Controller auf dem aktuellen Element. Wirf einen Fehler, wenn er nicht gefunden wird.

? - Versuchen Sie, den erforderlichen Controller zu finden, oder übergeben Sie null an den Link fn, wenn er nicht gefunden wird.

^ - Suchen Sie den gewünschten Controller, indem Sie das Element und seine übergeordneten Elemente durchsuchen. Wirf einen Fehler, wenn er nicht gefunden wird.

^^ - Suchen Sie den gewünschten Controller, indem Sie die Eltern des Elements durchsuchen. Wirf einen Fehler, wenn er nicht gefunden wird.

? ^ - Versuchen Sie, den erforderlichen Controller zu finden, indem Sie das Element und seine übergeordneten Elemente durchsuchen, oder übergeben Sie null an den Link fn, wenn er nicht gefunden wird.

? ^^ - Versuchen Sie, den erforderlichen Controller zu finden, indem Sie die übergeordneten Elemente des Elements durchsuchen, oder übergeben Sie null an den Link fn, wenn er nicht gefunden wird.



Alte Antwort:

Sie müssen einen $controllerDienst einspeisen, um einen Controller in einem anderen Controller zu instanziieren. Beachten Sie jedoch, dass dies zu einigen Designproblemen führen kann. Sie können jederzeit wiederverwendbare Services erstellen, die der Einzelverantwortung folgen, und diese nach Bedarf in die Controller einfügen.

Beispiel:

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

In jedem Fall können Sie nicht aufrufen, TestCtrl1.myMethod()da Sie die Methode an die $scopeund nicht an die Controller-Instanz angehängt haben .

Wenn Sie den Controller gemeinsam nutzen, ist es immer besser, Folgendes zu tun:

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

und während des Konsums tun:

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

Im ersten Fall ist das wirklich $scopeIhr Ansichtsmodell und im zweiten Fall die Controller-Instanz selbst.

PSL
quelle
4
Und es hängt von der vom Controller bereitgestellten Funktionalität ab. Wenn Sie es eher wie ein Ansichtsmodell gestalten, das Sie komponentenübergreifend teilen müssen, ist das in Ordnung, aber wenn es eher eine Dienstanbieterfunktionalität ist, würde ich einfach einen Dienst erstellen .
PSL
Sollte var testCtrl1ViewModel = $scope.$new();sein var testCtrl1ViewModel = $rootScope.$new();? Siehe : docs.angularjs.org/guide/controller @PSL
leonsPAPA
Im obigen Beispiel greifen Sie auf den Container auf dem Direktiven-Controller zu, aber ich kann dies nicht zum Laufen bringen. Ich kann über den vierten Parameter meiner Link-Funktion in der Direktive selbst auf die erforderlichen Controller zugreifen. Sie sind jedoch nicht wie im obigen Beispiel an den Direktiven-Controller gebunden. Hat noch jemand dieses Problem?
Sammi
33

Ich würde vorschlagen, dass die Frage, die Sie stellen sollten, ist, wie Dienste in Controller eingefügt werden. Fat Services mit Skinny Controllern sind eine gute Faustregel, auch bekannt als Controller, um Ihren Service / Ihre Fabrik (mit der Geschäftslogik) in Ihre Ansichten zu integrieren.

Controller erhalten Müll, der bei Routenänderungen gesammelt wird. Wenn Sie beispielsweise Controller verwenden, um Geschäftslogik zu speichern, die einen Wert wiedergibt, verlieren Sie auf zwei Seiten den Status, wenn der App-Benutzer auf die Schaltfläche "Zurück" des Browsers klickt.

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

Hier ist eine funktionierende Demo der Fabrik, die in zwei Controller injiziert wurde

Außerdem würde ich vorschlagen, dieses Tutorial über Dienstleistungen / Fabriken zu lesen.

frecher Bastard
quelle
13

Es ist nicht erforderlich, Ihren Controller in JS zu importieren / zu injizieren. Sie können Ihren Controller / verschachtelten Controller einfach über Ihr HTML einfügen. Es hat bei mir funktioniert. Mögen :

<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>
Chetanpawar
quelle
2
stimmt ... aber ich finde es immer noch besser, alle gemeinsamen Elemente in einen Dienst zu integrieren und den Dienst dem jeweiligen Controller zuzuführen.
Neel
-1
<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

Dies funktioniert am besten in meinem Fall, in dem TestCtrl2 eigene Anweisungen hat.

var testCtrl2 = $controller('TestCtrl2')

Dies gibt mir einen Fehler mit der Meldung scopeProvider-Injektionsfehler.

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

Dies funktioniert nicht wirklich, wenn Sie Direktiven in 'TestCtrl1' haben. Diese Direktive hat tatsächlich einen anderen Bereich als den hier erstellten. Sie erhalten zwei Instanzen von 'TestCtrl1'.

binRAIN
quelle
-1

Die beste Lösung:-

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

// Hier haben Sie den ersten Controller-Aufruf erhalten, ohne ihn auszuführen

Atul Singh
quelle
-1

Sie können auch $rootScopeeine Funktion / Methode des 1. Controllers vom zweiten Controller wie folgt aufrufen:

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

.controller('ctrl2', function($rootScope, $scope) {
     $rootScope.methodOf2ndCtrl = function() {
     //Your code here. 
}
})
user5943763
quelle
1
Downvote: Dies ist nur eine schlechte Codierung: Sie machen Ihre Funktion nur global. Lassen Sie Angular besser vollständig fallen, wenn Sie auf diese Weise codieren möchten ... Verwenden Sie einen Dienst, wie in den meisten anderen Antworten vorgeschlagen.
HammerNL
Dies wird nicht empfohlen. $ rootScope macht Code ungeschickt und führt langfristig zu Problemen.
Harshit Pant
-2

Verwenden Sie Typoskript für Ihre Codierung, da es objektorientiert, streng typisiert und einfach zu pflegen ist ...

Für weitere Informationen über Typoskript klicken Sie hier

Hier ein einfaches Beispiel, das ich erstellt habe, um Daten mit Typescript zwischen zwei Controllern auszutauschen ...

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
quelle