Как мне вставить контроллер в другой контроллер в 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
что, если бы мне пришлось обновить свойство контроллера, которое привязано к представлению. На это свойство влияет событие, происходящее в другом контроллере.
Анкит Танна, 02

Ответы:

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>

Есть много способов настроить 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ваша модель представления, а во втором - сам экземпляр контроллера.

PSL
источник
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
правда ... но я по-прежнему считаю, что лучше поместить все общие элементы в службу и внедрить службу в соответствующий контроллер.
Neel
-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.

binRAIN
источник
-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. 
}
})
user5943763
источник
1
Голос против: это просто плохой код: вы просто делаете свою функцию глобальной. Лучше полностью отказаться от Angular, если вы хотите кодировать таким образом ... Используйте службу, как предлагается в большинстве других ответов.
HammerNL
Это не рекомендуется. $ rootScope делает код неуклюжим и в долгосрочной перспективе приводит к проблемам.
Harshit Pant 06
-2

используйте машинописный текст для написания кода, потому что он объектно-ориентированный, строго типизированный и простой в обслуживании кода ...

для получения дополнительной информации о типах нажмите здесь

Вот один простой пример, который я создал для обмена данными между двумя контроллерами с помощью 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
источник