AngularJS控制器可以从同一模块中的另一个控制器继承吗?


198

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

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



2
撇开:这看起来不像继承,而更像是共享方法或注入。也许只是语义。
alockwood05年

该示例的链接不再有效。
AlexS 2015年

Google缓存链接:webcache.googleusercontent.com/…指向这个有趣的小提琴:jsfiddle.net/mhevery/u6s88/12
Federico Elles

Answers:


289

是的,可以,但是您必须使用该$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
贡塔德2014年

@gontard:在这种情况下,它必须是控制器,因为$controller只能使用注册的控制器。
ZeissS 2014年

10
这是一个很好的解决方案。谢谢。但是,如果我使用Controller As语法,该怎么办?
2014年

1
上面的小提琴是一个问题。值得注意的是controllerAs只是将控制器分配给了范围-因此您将更$scope改为this(理论上)
Dan Pantry 2015年

4
这对我有用,但是我尝试以在同一页面上具有父控制器和子控制器的方式来执行此操作。这将导致父控制器中的$ http操作运行两次。当子控制器注入父控制器的作用域时,我的$ scope.AllMembers数组将被填充两次,因为父控制器使它运行,然后子控制器使它再次运行。有什么办法可以防止这种情况?
瑞安·曼

20

如果您使用vm控制器语法,这是我的解决方案:

.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'...)来将当前上下文传递给闭包(for reload())函数,因此,只有一种解决方案是使用this继承的函数内部以动态更改上下文。


难道您不是刚刚这样做了吗?> $ controller('BaseGenericControl',{vm:vm});
herringtown '16

vm只是控制器内部的一个变量,我不认为Angular可以按预期使用它。
IProblemFactory

8

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

这是类似的问题---> AngularJS控制器继承


是的,这是一种方式,谢谢。我在寻找解决方案时碰到了那个帖子。我在想是否有某种方法可以加载控制器功能并使用它扩展“ this”。
到2014年

我想要一个通用loading变量,以便在加载数据时我总是做同样的事情,我认为工厂不能做到这一点。我的父控制器可以有一个加载变量,但是工厂无法对其进行操作...对吗?
PixMach 2015年

7

针对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定义,则用于child.检索它。

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

5

好吧,我以另一种方式做到了。就我而言,我想要一个在其他控制器中应用相同功能和属性的功能。我喜欢它,除了参数。这样,您所有的ChildCtrls都需要接收$ 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();
});

4

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

使用以下方法:

父组件(扩展自):

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

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


2

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

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

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

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

如注释中所述,您可以将$ 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;
});

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


使用“ controller as”语法没有问题。见我的答案:stackoverflow.com/a/36549465/2197555
gm2008 '16

2

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

使用IProblemFactorySalman Abbas的答案,我进行了以下操作以访问父变量:

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


0

您可以使用简单的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);

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.