Как да инжектирам контролер в друг контролер в AngularJS

Нов съм в Angular и се опитвам да разбера как да правя нещата...

Използвайки AngularJS, как мога да инжектирам контролер, който да се използва в друг контролер?

Имам следния фрагмент:

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

Когато изпълня това, получавам грешката:

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

Трябва ли дори да се опитвам да използвам контролер вътре в друг контролер или трябва да направя това услуга?


person Scottie    schedule 21.08.2014    source източник
comment
Не можете да инжектирате контролери един в друг. Да, вместо това трябва да промените TestCtrl1 в услуга.   -  person Sly_cardinal    schedule 21.08.2014
comment
Точно така, използвайте услугите   -  person Miguel Mota    schedule 21.08.2014
comment
какво ще стане, ако трябва да актуализирам свойство на контролер, който се свързва с изгледа. Това свойство е повлияно от събитието, случващо се в друг контролер.   -  person Ankit Tanna    schedule 02.03.2016


Отговори (7)


Ако вашето намерение е да се сдобиете с вече инстанциран контролер на друг компонент и ако следвате подход, базиран на компонент/директива, винаги можете да require контролер (екземпляр на компонент) от друг компонент, който следва определена йерархия.

Например:

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

Сега използването на тези горни компоненти може да бъде нещо подобно:

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

Има много начини, по които можете да настроите require .

(без префикс) - Намерете необходимия контролер на текущия елемент. Извежда грешка, ако не бъде намерен.

? - Опитайте се да намерите необходимия контролер или подайте null на връзката fn, ако не бъде намерена.

^ - Намерете необходимия контролер, като потърсите елемента и неговите родители. Извежда грешка, ако не бъде намерен.

^^ - Намерете необходимия контролер, като потърсите родителите на елемента. Извежда грешка, ако не бъде намерен.

?^ - Опитайте се да намерите необходимия контролер чрез търсене в елемента и неговите родители или подайте null на връзката fn, ако не бъде намерена.

?^^ - Опитайте се да намерите необходимия контролер чрез търсене на родителите на елемента или подайте null на връзката fn, ако не бъде намерена.



Стар отговор:

Трябва да инжектирате $controller услуга, за да създадете контролер в друг контролер. Но имайте предвид, че това може да доведе до някои проблеми с дизайна. Винаги можете да създадете услуги за многократна употреба, които следват Единичната отговорност и да ги инжектирате в контролерите, както ви е необходимо.

Пример:

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

Във всеки случай не можете да извикате TestCtrl1.myMethod(), защото сте прикачили метода към $scope, а не към екземпляра на контролера.

Ако споделяте контролера, винаги би било по-добре да направите: -

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

и докато консумирате правете:

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

В първия случай наистина $scope е вашият модел на изглед, а във втория случай това е самият екземпляр на контролера.

person PSL    schedule 21.08.2014
comment
И това зависи от функционалността, осигурена от контролера, ако го правите като по-скоро модел на изглед, който трябва да споделите между компонентите, това е добре, но ако това е повече от функционалност на доставчик на услуги, тогава просто ще отида със създаването на услуга . - person PSL; 21.08.2014
comment
Трябва ли var testCtrl1ViewModel = $scope.$new(); да бъде var testCtrl1ViewModel = $rootScope.$new();? вижте: docs.angularjs.org/guide/controller @PSL - person leonsPAPA; 27.11.2015
comment
В примера по-горе вие ​​осъществявате достъп до контейнер на контролера на директиви, но не мога да накарам това да работи. Мога да получа достъп до необходимите контролери чрез четвъртия параметър на моята функция за връзка в самата директива. Но те не са обвързани с контролера на директивата, както в примера по-горе. Някой друг да има този проблем? - person Sammi; 12.10.2017

Бих предложил въпросът, който трябва да зададете, е как да инжектирате услуги в контролери. Дебелите услуги с тънки контролери е добро правило, известно още като просто използвайте контролери, за да залепите вашата услуга/фабрика (с бизнес логиката) във вашите изгледи.

Контролерите събират боклук при промени в маршрута, така че например, ако използвате контролери за поддържане на бизнес логика, която изобразява стойност, ще загубите състояние на две страници, ако потребителят на приложението щракне върху бутона за връщане назад на браузъра.

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

Ето работеща демонстрация на фабрично инжектирана в два контролера

Също така бих предложил да прочетете този урок за услуги/фабрики.

person cheekybastard    schedule 21.08.2014

Няма нужда да импортирате/инжектирате вашия контролер в JS. Можете просто да инжектирате вашия контролер/вложен контролер през вашия HTML. При мен свърши работа. Като :

<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>
person chetanpawar    schedule 26.05.2015
comment
вярно... но все още смятам, че е по-добре да поставя всички общи елементи в услуга и да инжектирам услугата към съответния контролер. - person Neel; 28.06.2015

можете също да използвате $rootScope, за да извикате функция/метод на 1-ви контролер от втори контролер като този,

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

.controller('ctrl2', function($rootScope, $scope) {
     $rootScope.methodOf2ndCtrl = function() {
     //Your code here. 
}
})
person user5943763    schedule 31.08.2016
comment
Глас против: Това е просто лошо кодиране: просто правите функцията си глобална. По-добре изоставете Angular напълно, ако това е начинът, по който искате да кодирате... Използвайте услуга, както е предложено от повечето други отговори. - person HammerNL; 25.10.2016
comment
Това не се препоръчва. $rootScope прави кода тромав и води до проблеми в дългосрочен план. - person Harshit Pant; 06.12.2018

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

Това работи най-добре в моя случай, където TestCtrl2 има свои собствени директиви.

var testCtrl2 = $controller('TestCtrl2')

Това ми дава грешка, казваща грешка при инжектиране на scopeProvider.

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

Това наистина не работи, ако имате директиви в „TestCtrl1“, тази директива всъщност има различен обхват от този, създаден тук. В крайна сметка получавате два екземпляра на „TestCtrl1“.

person binRAIN    schedule 09.07.2015

Най-доброто решение: -

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

// Тук получавате първото извикване на контролера, без да го изпълните

person Atul Singh    schedule 26.08.2016

използвайте typescript за вашето кодиране, защото е обектно ориентиран, строго въведен и лесен за поддържане на кода ...

за повече информация относно typescipt щракнете тук

Ето един прост пример, който създадох за споделяне на данни между два контролера с помощта на 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);

}

person UniCoder    schedule 15.09.2016