Come inserisco un controller in un altro controller in AngularJS


97

Sono nuovo su Angular e sto cercando di capire come fare le cose ...

Utilizzando AngularJS, come posso iniettare un controller da utilizzare all'interno di un altro controller?

Ho il seguente frammento:

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

Quando lo eseguo, ottengo l'errore:

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

Dovrei anche provare a utilizzare un controller all'interno di un altro controller o dovrei renderlo un servizio?


2
Non puoi iniettare controller l'uno nell'altro. Sì, dovresti invece trasformarti TestCtrl1in un servizio.
Sly_cardinal

Esatto, usa i servizi
Miguel Mota

3
e se dovessi aggiornare una proprietà di un controller che si lega alla vista. Questa proprietà è influenzata dall'evento che si verifica in un altro controller.
Ankit Tanna

Risposte:


129

Se la tua intenzione è di entrare in possesso di un controller già istanziato di un altro componente e se stai seguendo un approccio basato su componenti / direttive, puoi sempre requireun controller (istanza di un componente) da un altro componente che segue una certa gerarchia.

Per esempio:

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

Ora l'utilizzo di questi componenti di cui sopra potrebbe essere qualcosa del genere:

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

Ci sono molti modi in cui puoi configurare require .

(nessun prefisso) - Individua il controller richiesto sull'elemento corrente. Genera un errore se non viene trovato.

? - Tentare di individuare il controller richiesto o di passare null al collegamento fn se non trovato.

^ - Individua il controller richiesto cercando l'elemento e i suoi genitori. Genera un errore se non viene trovato.

^^ - Individua il controller richiesto cercando i genitori dell'elemento. Genera un errore se non viene trovato.

? ^ - Tenta di individuare il controller richiesto cercando l'elemento e i suoi genitori o passa null al collegamento fn se non trovato.

? ^^ - Tenta di individuare il controller richiesto cercando i genitori dell'elemento o passa null al collegamento fn se non trovato.



Vecchia risposta:

È necessario iniettare il $controllerservizio per creare un'istanza di un controller all'interno di un altro controller. Ma tieni presente che questo potrebbe portare ad alcuni problemi di progettazione. Puoi sempre creare servizi riutilizzabili che seguono la Responsabilità Unica e iniettarli nei controller quando necessario.

Esempio:

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 ogni caso non puoi chiamare TestCtrl1.myMethod()perché hai allegato il metodo sull'istanza del controller $scopee non sull'istanza.

Se condividi il controller, sarebbe sempre meglio fare: -

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

e mentre consumi fai:

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

Nel primo caso è realmente il $scopetuo modello di visualizzazione e nel secondo caso è l'istanza del controller stessa.


4
E dipende dalla funzionalità fornita dal controller, se lo stai rendendo più simile a un modello di visualizzazione che devi condividere tra i componenti, va bene, ma se si tratta più di una funzionalità di un fornitore di servizi, allora vorrei semplicemente creare un servizio .
PSL

Dovrebbe var testCtrl1ViewModel = $scope.$new();essere var testCtrl1ViewModel = $rootScope.$new();? fare riferimento a: docs.angularjs.org/guide/controller @PSL
leonsPAPA

Nell'esempio sopra si accede al container sul controller di direttiva, ma non riesco a farlo funzionare. Posso accedere ai controller richiesti tramite il quarto parametro sulla mia funzione di collegamento sulla direttiva stessa. Ma non sono vincolati al controller di direttiva come nell'esempio sopra. Qualcun altro ha questo problema?
Sammi

33

Suggerirei che la domanda che dovresti porre è come iniettare servizi nei controller. I servizi pesanti con controller skinny sono una buona regola pratica, ovvero usa semplicemente i controller per incollare il tuo servizio / fabbrica (con la logica di business) nelle tue visualizzazioni.

I controller vengono raccolti in modo indesiderato durante le modifiche del percorso, quindi, ad esempio, se si utilizzano controller per mantenere la logica di business che esegue il rendering di un valore, si perderà lo stato su due pagine se l'utente dell'app fa clic sul pulsante Indietro del browser.

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

Ecco una demo funzionante della fabbrica iniettata in due controller

Inoltre, suggerirei di leggere questo tutorial sui servizi / fabbriche.


13

Non è necessario importare / iniettare il controller in JS. Puoi semplicemente iniettare il tuo controller / controller annidato attraverso il tuo HTML, ha funzionato per me. Piace :

<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

2
vero ... ma ritengo comunque che sia meglio mettere tutti gli elementi comuni in un servizio e iniettare il servizio al rispettivo controller.
Neel

-1
<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

Questo funziona meglio nel mio caso, dove TestCtrl2 ha le proprie direttive.

var testCtrl2 = $controller('TestCtrl2')

Questo mi dà un errore che dice errore di iniezione scopeProvider.

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

Questo non funziona davvero se hai direttive in "TestCtrl1", quella direttiva in realtà ha un ambito diverso da quello creato qui. Finisci con due istanze di "TestCtrl1".


-1

La migliore soluzione:-

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

// Qui hai la prima chiamata al controller senza eseguirla


-1

puoi anche usare $rootScopeper chiamare una funzione / metodo del primo controller dal secondo controller come questo,

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

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

1
Downvote: Questa è solo una cattiva codifica: stai solo rendendo la tua funzione globale. È meglio eliminare completamente Angular se questo è il modo in cui si desidera codificare ... Utilizzare un servizio come suggerito dalla maggior parte delle altre risposte.
HammerNL

Questo non è consigliato. $ rootScope rende il codice goffo e porta a problemi a lungo termine.
Harshit Pant

-2

usa il dattiloscritto per la tua codifica, perché è orientato agli oggetti, rigorosamente digitato e facile da mantenere il codice ...

per maggiori informazioni sulla tipizzazione clicca qui

Ecco un semplice esempio che ho creato per condividere i dati tra due controller utilizzando 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);

}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.