【问题标题】:Can an AngularJS controller inherit from another controller in the same module?AngularJS 控制器可以从同一模块中的另一个控制器继承吗?
【发布时间】:2013-08-29 22:54:09
【问题描述】:

在模块内,控制器可以从外部控制器继承属性:

var app = angular.module('angularjs-starter', []);

var ParentCtrl = function ($scope, $location) {
};

app.controller('ChildCtrl', function($scope, $injector) {
  $injector.invoke(ParentCtrl, this, {$scope: $scope});
});

示例来自:死链接http://blog.omkarpatil.com/2013/02/controller-inheritance-in-angularjs.html

模块内的控制器也可以从兄弟继承吗?

var app = angular.module('angularjs-starter', []);

app.controller('ParentCtrl ', function($scope) {
  //I'm the sibling, but want to act as parent
});

app.controller('ChildCtrl', function($scope, $injector) {
  $injector.invoke(ParentCtrl, this, {$scope: $scope}); //This does not work
});

第二个代码不起作用,因为$injector.invoke 需要一个函数作为第一个参数并且找不到对ParentCtrl 的引用。

【问题讨论】:

标签: angularjs inheritance angularjs-controller


【解决方案1】:

是的,可以,但您必须使用$controller 服务来实例化控制器:-

var app = angular.module('angularjs-starter', []);

app.controller('ParentCtrl', function($scope) {
  // I'm the sibling, but want to act as parent
});

app.controller('ChildCtrl', function($scope, $controller) {
  $controller('ParentCtrl', {$scope: $scope}); //This works
});

【讨论】:

  • ParentCtrl 应该是 controller 还是可以使用 service
  • @gontard:在这种情况下它必须是一个控制器,因为$controller 只能使用注册的控制器。
  • 这是一个非常好的解决方案。谢谢你。但是如果我使用 Controller As 语法,我该怎么做呢?
  • 上述小提琴是作为一个问题提出的。值得注意的是,controllerAs 只是将控制器分配给范围 - 所以你可以将 $scope 更改为 this(理论上)
  • 这对我有用,但是我正在尝试以一种方式将父控制器和子控制器放在同一页面上。这会导致父控制器中的 $http 操作运行两次。当子控制器注入父控制器的范围时,我的 $scope.AllMembers 数组被填充两次,因为父控制器使其运行,然后子控制器使其再次运行。有什么办法可以防止吗?
【解决方案2】:

如果您使用vm controller syntax,这是我的解决方案:

.controller("BaseGenericCtrl", function ($scope) {

    var vm = this;
    vm.reload = reload;
    vm.items = [];

    function reload() {
        // this function will come from child controller scope - RESTDataService.getItemsA
        this.getItems();
    }
})

.controller("ChildCtrl", function ($scope, $controller, RESTDataService) {
    var vm = this;
    vm.getItems = RESTDataService.getItemsA;
    angular.extend(vm, $controller('BaseGenericCtrl', {$scope: $scope}));
})

不幸的是,您不能使用$controller.call(vm, 'BaseGenericCtrl'...) 将当前上下文传递给闭包(对于reload())函数,因此只有一种解决方案是在继承函数中使用this 以动态更改上下文。

【讨论】:

  • 你就不能这样做吗? > $controller('BaseGenericControl', { vm: vm });
  • vm 只是控制器内部的一个变量,我不认为 Angular 可以按预期使用它。
【解决方案3】:

我认为,您应该使用工厂或服务,为两个控制器提供可访问的功能或数据。

这里有类似的问题 ---> AngularJS controller inheritance

【讨论】:

  • 是的,这是一种方法,谢谢。我在寻找解决方案时遇到了那个帖子。我在想是否有某种方法可以加载控制器功能并用它扩展“this”。
  • 我想要一个通用的loading 变量,以便在加载数据时我总是做同样的事情,我认为工厂做不到。我的父控制器可以有一个加载变量,但工厂不能操纵它......对吧?!
【解决方案4】:

针对this answer by gmontague 中提出的问题,我找到了一种使用$controller() 继承控制器的方法,并且仍然使用控制器“as”语法。

首先,在继承调用 $controller() 时使用“as”语法:

    app.controller('ParentCtrl', function(etc...) {
        this.foo = 'bar';
    });
    app.controller('ChildCtrl', function($scope, $controller, etc...) {
        var ctrl = $controller('ParentCtrl as parent', {etc: etc, ...});
        angular.extend(this, ctrl);

    });

然后,在 HTML 模板中,如果属性是由 parent 定义的,则使用parent. 检索从 parent 继承的属性;如果由孩子定义,则使用child. 检索它。

    <div ng-controller="ChildCtrl as child">{{ parent.foo }}</div>

【讨论】:

    【解决方案5】:

    好吧,我以另一种方式做到了这一点。就我而言,我想要一个在其他控制器中应用相同功能和属性的功能。我喜欢它,除了参数。这样,你所有的 ChildCtrl 都需要接收 $location。

    var app = angular.module('angularjs-starter', []);
    
    function BaseCtrl ($scope, $location) {
        $scope.myProp = 'Foo';
        $scope.myMethod = function bar(){ /* do magic */ };
    }
    
    app.controller('ChildCtrl', function($scope, $location) {
        BaseCtrl.call(this, $scope, $location);
    
        // it works
        $scope.myMethod();
    });
    

    【讨论】:

      【解决方案6】:

      对于那些想知道的人,您可以使用已接受答案中的方法以相同的方式扩展组件控制器。

      使用以下方法:

      父组件(扩展自):

      /**
       * Module definition and dependencies
       */
      angular.module('App.Parent', [])
      
      /**
       * Component
       */
      .component('parent', {
        templateUrl: 'parent.html',
        controller: 'ParentCtrl',
      })
      
      /**
       * Controller
       */
      .controller('ParentCtrl', function($parentDep) {
      
        //Get controller
        const $ctrl = this;
      
        /**
         * On init
         */
        this.$onInit = function() {
      
          //Do stuff
          this.something = true;
        };
      });
      

      子组件(扩展的那个):

      /**
       * Module definition and dependencies
       */
      angular.module('App.Child', [])
      
      /**
       * Component
       */
      .component('child', {
        templateUrl: 'child.html',
        controller: 'ChildCtrl',
      })
      
      /**
       * Controller
       */
      .controller('ChildCtrl', function($controller) {
      
        //Get controllers
        const $ctrl = this;
        const $base = $controller('ParentCtrl', {});
        //NOTE: no need to pass $parentDep in here, it is resolved automatically
        //if it's a global service/dependency
      
        //Extend
        angular.extend($ctrl, $base);
      
        /**
         * On init
         */
        this.$onInit = function() {
      
          //Call parent init
          $base.$onInit.call(this);
      
          //Do other stuff
          this.somethingElse = true;
        };
      });
      

      诀窍是使用命名控制器,而不是在组件定义中定义它们。

      【讨论】:

        【解决方案7】:

        如已接受的答案中所述,您可以通过在您的子控制器中调用:$controller('ParentCtrl', {$scope: $scope, etc: etc});“继承”父控制器对 $scope 和其他服务的修改。

        但是,如果您习惯于使用控制器的“as”语法,例如在

        <div ng-controller="ChildCtrl as child">{{ child.foo }}</div>
        

        如果在父控制器中设置了foo(通过this.foo = ...),子控制器将无权访问它。

        如 cmets 中所述,您可以将 $controller 的结果直接分配给作用域:

        var app = angular.module('angularjs-starter', []);
        app.controller('ParentCtrl ', function(etc...) {
            this.foo = 'bar';
        });
        app.controller('ChildCtrl', function($scope, $controller, etc...) {
            var inst = $controller('ParentCtrl', {etc: etc, ...});
        
            // Perform extensions to inst
            inst.baz = inst.foo + " extended";
        
            // Attach to the scope
            $scope.child = inst;
        });
        

        注意:然后您必须ng-controller= 中删除“as”部分,因为您在代码中指定实例名称,而不是模板。

        【讨论】:

        【解决方案8】:

        我在vm = this 中使用“Controller as”语法并想继承一个控制器。如果我的父控制器有一个修改变量的函数,我会遇到问题。

        使用 IProblemFactory'sSalman Abbas's 答案,我做了以下操作来访问父变量:

        (function () {
          'use strict';
          angular
              .module('MyApp',[])
              .controller('AbstractController', AbstractController)
              .controller('ChildController', ChildController);
        
          function AbstractController(child) {
            var vm = child;
            vm.foo = 0;
            
            vm.addToFoo = function() {
              vm.foo+=1;
            }
          };
          
          function ChildController($controller) {
            var vm = this;
            angular.extend(vm, $controller('AbstractController', {child: vm}));
          };
        })();
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
        <div ng-controller="ChildController as childCtrl" layout="column" ng-cloak="" ng-app="MyApp">
          <button type="button" ng-click="childCtrl.addToFoo()">
            add
          </button>
          <span>
              -- {{childCtrl.foo}} --
          </span>
        </div>

        【讨论】:

          【解决方案9】:

          您可以使用简单的 JavaScript 继承机制。也不要忘记传递一个必要的角度服务来调用 .call 方法。

          //simple function (js class)
          function baseCtrl($http, $scope, $location, $rootScope, $routeParams, $log, $timeout, $window, modalService) {//any serrvices and your 2
          
             this.id = $routeParams.id;
             $scope.id = this.id;
          
             this.someFunc = function(){
                $http.get("url?id="+this.id)
                .then(success function(response){
                  ....
                 } ) 
          
             }
          ...
          }
          
          angular
                  .module('app')
                  .controller('childCtrl', childCtrl);
          
          //angular controller function
          function childCtrl($http, $scope, $location, $rootScope, $routeParams, $log, $timeout, $window, modalService) {      
             var ctrl = this;
             baseCtrl.call(this, $http, $scope, $location, $rootScope,  $routeParams, $log, $timeout, $window, modalService);
          
             var idCopy = ctrl.id;
             if($scope.id == ctrl.id){//just for sample
                ctrl.someFunc();
             }
          }
          
          //also you can copy prototype of the base controller
          childCtrl.prototype = Object.create(baseCtrl.prototype);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-03-06
            • 2016-01-05
            • 2014-01-16
            • 2011-08-19
            • 2014-08-27
            • 2013-03-01
            • 2017-09-30
            • 1970-01-01
            相关资源
            最近更新 更多