Як ввести контролер в інший контролер в AngularJS


97

Я новачок у 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

Чи мені взагалі намагатися використовувати контролер всередині іншого контролера, чи я повинен зробити це послугою?


2
Ви не можете вводити контролери один в одного. Так, TestCtrl1замість цього вам слід перевтілитися у послугу.
Sly_cardinal

Точно, користуйся послугами
Мігель Мота

3
що, якби мені довелося оновити властивість контролера, який прив’язується до подання. На цю властивість впливає подія, що відбувається в іншому контролері.
Ankit Tanna

Відповіді:


129

Якщо ви маєте намір отримати вже створений контролер іншого компонента, і якщо ви дотримуєтесь підходу, заснованого на компонентах / директивах, ви завжди 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>

Є багато способів , які можна запрограмувати потрібно .

(без префікса) - знайдіть необхідний контролер на поточному елементі. Видаліть помилку, якщо не знайдено.

? - Спроба знайти необхідний контролер або передати 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ваша модель подання, а у другому випадку це сам екземпляр контролера.


4
І це залежить від функціональних можливостей, що надаються контролером. Якщо ви робите це більше схожим на модель перегляду, якою вам потрібно поділитися між компонентами, це нормально, але якщо це більше функціональність постачальника послуг, тоді я просто піду на створення служби .
PSL,

Має var testCtrl1ViewModel = $scope.$new();бути var testCtrl1ViewModel = $rootScope.$new();? зверніться до: docs.angularjs.org/guide/controller @PSL
leonsPAPA

У наведеному вище прикладі ви отримуєте доступ до контейнера на контролері директив, але я не можу змусити це працювати. Я можу отримати доступ до необхідних контролерів через четвертий параметр моєї функції зв’язку в самій директиві. Але вони не прив'язані до контролера директив, як у прикладі вище. Хто-небудь ще мав цю проблему?
Саммі

33

Я б запропонував запитання, яке ви повинні задати, - як вводити послуги в контролери. Товсті послуги з худими контролерами - це хороше правило, а саме просто використовуйте контролери, щоб прикріпити вашу службу / фабрику (з діловою логікою) до ваших поглядів.

Контролери отримують сміття, зібране при зміні маршруту, тому, наприклад, якщо ви використовуєте контролери для утримання бізнес-логіки, яка надає значення, ви втратите стан на двох сторінках, якщо користувач програми натисне кнопку повернення браузера.

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

Ось робоча демонстрація заводу, введена у два контролери

Крім того, я пропоную прочитати цей підручник про послуги / фабрики.


13

Немає необхідності імпортувати / вводити ваш контролер у JS. Ви можете просто ввести ваш контролер / вкладений контролер через ваш HTML. Це спрацювало для мене. Подібно до :

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

2
правда ... але я все одно вважаю, що краще вкласти всі загальні елементи в службу і ввести послугу відповідному контролеру.
Ніл

-1
<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".


-1

Найкраще рішення: -

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

// Тут ви отримали перший виклик контролера без його виконання


-1

Ви також можете використовувати $rootScopeдля виклику функції / методу 1-го контролера з другого контролера, як це,

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

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

1
Голос проти: Це просто погане кодування: ви просто робите свою функцію глобальною. Краще повністю відкиньте Angular, якщо це спосіб, яким ви хочете кодувати ... Скористайтеся послугою, як пропонується більшістю інших відповідей.
HammerNL

Це не рекомендується. $ rootScope робить код незграбним і призводить до проблем у довгостроковій перспективі.
Harshit Pant

-2

використовуйте машинопис для кодування, оскільки він об’єктно-орієнтований, строго набраний і простий в обслуговуванні код ...

для отримання додаткової інформації про typecipt натисніть тут

Ось один простий приклад, який я створив для обміну даними між двома контролерами за допомогою 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);

}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.