¿Cómo inyecto un controlador en otro controlador en AngularJS?

97

Soy nuevo en Angular y trato de descubrir cómo hacer las cosas ...

Usando AngularJS, ¿cómo puedo inyectar un controlador para usar dentro de otro controlador?

Tengo el siguiente fragmento:

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

Cuando ejecuto esto, aparece el error:

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

¿Debería intentar usar un controlador dentro de otro controlador, o debería hacer de esto un servicio?

Escocés
fuente
2
No se pueden inyectar controladores entre sí. Sí, debería cambiarse TestCtrl1a un servicio.
Sly_cardinal
Exactamente, use los servicios
Miguel Mota
3
¿Qué pasa si tuviera que actualizar una propiedad de un controlador que se une a la vista? Esta propiedad se ve afectada por el evento que ocurre en otro controlador.
Ankit Tanna

Respuestas:

129

Si su intención es obtener un controlador ya instanciado de otro componente y si está siguiendo un enfoque basado en componentes / directivas, siempre puede obtener requireun controlador (instancia de un componente) de otro componente que sigue una determinada jerarquía.

Por ejemplo:

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

Ahora, el uso de estos componentes anteriores podría ser algo como esto:

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

Hay muchas formas de configurar require .

(sin prefijo): localice el controlador requerido en el elemento actual. Lanza un error si no se encuentra.

? - Intente ubicar el controlador requerido o pase nulo al enlace fn si no lo encuentra.

^ - Localice el controlador requerido buscando el elemento y sus padres. Lanza un error si no se encuentra.

^^ - Localice el controlador requerido buscando los padres del elemento. Lanza un error si no se encuentra.

? ^ - Intente ubicar el controlador requerido buscando el elemento y sus padres o pase nulo al enlace fn si no lo encuentra.

? ^^ - Intente ubicar el controlador requerido buscando en los padres del elemento, o pase nulo al enlace fn si no lo encuentra.



Respuesta anterior:

Debe inyectar el $controllerservicio para crear una instancia de un controlador dentro de otro controlador. Pero tenga en cuenta que esto puede provocar algunos problemas de diseño. Siempre puede crear servicios reutilizables que sigan la responsabilidad única e inyectarlos en los controladores según lo necesite.

Ejemplo:

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

En cualquier caso, no puede llamar TestCtrl1.myMethod()porque ha adjuntado el método en la $scopeinstancia del controlador y no en la.

Si está compartiendo el controlador, siempre será mejor hacer: -

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

y mientras consumes:

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

En el primer caso, realmente $scopees su modelo de vista, y en el segundo caso es la instancia del controlador en sí.

PSL
fuente
4
Y depende de la funcionalidad proporcionada por el controlador, si lo está convirtiendo más en un modelo de vista que necesita compartir entre componentes, está bien, pero si se trata más de una funcionalidad de proveedor de servicios, entonces simplemente crearía un servicio. .
PSL
¿Debería var testCtrl1ViewModel = $scope.$new();ser var testCtrl1ViewModel = $rootScope.$new();? consulte: docs.angularjs.org/guide/controller @PSL
leonsPAPA
En el ejemplo anterior, está accediendo al contenedor en el controlador de la directiva, pero no puedo hacer que esto funcione. Puedo acceder a los controladores necesarios a través del cuarto parámetro en mi función de enlace en la propia directiva. Pero no están vinculados al controlador de la directiva como en el ejemplo anterior. ¿Alguien más tiene este problema?
Sammi
33

Sugeriría que la pregunta que debería hacerse es cómo inyectar servicios en los controladores. Los servicios gordos con controladores delgados son una buena regla general, también conocida como simplemente use controladores para pegar su servicio / fábrica (con la lógica comercial) en sus puntos de vista.

Los controladores obtienen la basura recolectada en los cambios de ruta, por lo que, por ejemplo, si usa controladores para mantener la lógica comercial que representa un valor, perderá el estado en dos páginas si el usuario de la aplicación hace clic en el botón Atrás del navegador.

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

Aquí hay una demostración funcional de fábrica inyectada en dos controladores

Además, sugiero leer este tutorial sobre servicios / fábricas.

bastardo descarado
fuente
13

No es necesario importar / inyectar su controlador en JS. Simplemente puede inyectar su controlador / controlador anidado a través de su HTML.A mí me funcionó. Me gusta :

<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>
chetanpawar
fuente
2
cierto ... pero sigo sintiendo que es mejor poner todos los elementos comunes en un servicio e inyectar el servicio al controlador respectivo.
Neel
-1
<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

Esto funciona mejor en mi caso, donde TestCtrl2 tiene sus propias directivas.

var testCtrl2 = $controller('TestCtrl2')

Esto me da un error que dice error de inyección de scopeProvider.

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

Esto realmente no funciona si tiene directivas en 'TestCtrl1', esa directiva en realidad tiene un alcance diferente de este creado aquí. Termina con dos instancias de 'TestCtrl1'.

binRAIN
fuente
-1

La mejor solucion:-

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

// Aquí tienes la primera llamada al controlador sin ejecutarla

Atul Singh
fuente
-1

también puede usar $rootScopepara llamar a una función / método del primer controlador desde el segundo controlador como este,

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

.controller('ctrl2', function($rootScope, $scope) {
     $rootScope.methodOf2ndCtrl = function() {
     //Your code here. 
}
})
usuario5943763
fuente
1
Votar en contra: Esto es simplemente una mala codificación: solo está haciendo que su función sea global. Es mejor eliminar Angular por completo si esta es la forma en que desea codificar ... Use un servicio como lo sugieren la mayoría de las otras respuestas.
HammerNL
No se recomienda. $ rootScope hace que el código sea torpe y genera problemas a largo plazo.
Harshit Pant
-2

use mecanografiado para su codificación, porque está orientado a objetos, estrictamente escrito y fácil de mantener el código ...

para más información sobre typescipt haga clic aquí

Aquí un ejemplo simple que he creado para compartir datos entre dos controladores usando 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
fuente