【问题标题】:How do I inject a controller into another controller in AngularJS如何将控制器注入AngularJS中的另一个控制器
【发布时间】:2014-10-14 13:05:34
【问题描述】:

我是 Angular 的新手,正在尝试弄清楚如何做事......

使用 AngularJS,我如何注入一个控制器以在另一个控制器中使用?

我有以下sn-p:

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

我什至应该尝试在另一个控制器中使用一个控制器,还是应该将此作为服务?

【问题讨论】:

  • 您不能将控制器相互注入。是的,您应该将TestCtrl1 改为服务。
  • 没错,使用服务
  • 如果我必须更新绑定到视图的控制器的属性怎么办。此属性受另一个控制器中发生的事件的影响。

标签: angularjs angularjs-scope angularjs-controller


【解决方案1】:

如果您的意图是获取另一个组件的已实例化控制器,并且如果您遵循基于组件/指令的方法,您始终可以 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 是您的视图模型,而在第二种情况下,它是控制器实例本身。

【讨论】:

  • 这取决于控制器提供的功能,如果你让它更像一个需要跨组件共享的视图模型,那很好,但如果它更像是服务提供者的功能,那么我只想创建一个服务。
  • 应该var testCtrl1ViewModel = $scope.$new();var testCtrl1ViewModel = $rootScope.$new(); 吗?参考:docs.angularjs.org/guide/controller@PSL
  • 在上面的示例中,您正在访问指令控制器上的容器,但我无法让它工作。我可以通过指令本身的链接函数的第四个参数访问所需的控制器。但它们并不像上面的示例那样绑定到指令控制器。其他人有这个问题吗?
【解决方案2】:

我建议您应该问的问题是如何将服务注入控制器。带有瘦控制器的胖服务是一个很好的经验法则,也就是使用控制器将您的服务/工厂(带有业务逻辑)粘合到您的视图中。

控制器会在路由更改时收集垃圾,例如,如果您使用控制器来保存呈现值的业务逻辑,那么如果应用用户单击浏览器后退按钮,您将丢失两个页面上的状态。

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

这是一个有效的demo of factory injected into two controllers

另外,我建议有一个read of this tutorial on services/factories.

【讨论】:

    【解决方案3】:

    无需在 JS 中导入/注入您的控制器。您可以通过 HTML 注入您的控制器/嵌套控制器。它对我有用。 喜欢:

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

    【讨论】:

    • 是的......但我仍然觉得将所有常见元素放入服务并将服务注入相应的控制器会更好。
    【解决方案4】:

    您也可以像这样使用$rootScope 从第二个控制器调用第一个控制器的函数/方法,

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

    【讨论】:

    • 投反对票:这只是糟糕的编码:你只是让你的函数全球化。如果这是您想要编码的方式,最好完全放弃 Angular... 使用大多数其他答案所建议的服务。
    • 不建议这样做。 $rootScope 使代码变得笨拙并导致长期出现问题。
    【解决方案5】:
    <div ng-controller="TestCtrl1">
        <div ng-controller="TestCtrl2">
          <!-- your code--> 
        </div> 
    </div>
    

    这在我的情况下效果最好,因为 TestCtrl2 有它自己的指令。

    var testCtrl2 = $controller('TestCtrl2')
    

    这给了我一个错误,提示 scopeProvider injection error。

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

    如果您在“TestCtrl1”中有指令,这实际上不起作用,该指令实际上与此处创建的指令具有不同的范围。 您最终会得到两个“TestCtrl1”实例。

    【讨论】:

      【解决方案6】:

      最佳解决方案:-

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

      // 这里你得到了第一个控制器调用,但没有执行它

      【讨论】:

        【解决方案7】:

        使用 typescript 进行编码,因为它是面向对象的、严格类型化的并且易于维护代码...

        有关 typecipt 的更多信息click here

        这是我创建的一个简单示例,用于使用 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);
        

        }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-18
          • 2015-08-18
          • 1970-01-01
          • 2014-09-09
          相关资源
          最近更新 更多