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

Posted

技术标签:

【中文标题】AngularJS 控制器可以从同一模块中的另一个控制器继承吗?【英文标题】:Can an AngularJS controller inherit from another controller in the same module? 【发布时间】: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 的引用。

【问题讨论】:

这应该会有所帮助:***.com/questions/16828287/… aside:这看起来不像继承,更像是共享方法或注入。也许只是语义。 该示例的链接不再有效。 Google 缓存链接:webcache.googleusercontent.com/… 指向这个有趣的 Fiddle:jsfiddle.net/mhevery/u6s88/12 【参考方案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”部分,因为您在代码中指定实例名称,而不是模板。

【讨论】:

使用“controller as”语法没有问题。看我的回答:***.com/a/36549465/2197555【参考方案8】:

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

使用 IProblemFactory's 和 Salman 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);

【讨论】:

以上是关于AngularJS 控制器可以从同一模块中的另一个控制器继承吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何从AngularJS中的另一个控制器调用函数? [复制]

如何在模块化的angularjs范围内测试指令?

从当前项目中的另一个模块启动 MainActivity

将数据从一个视图控制器传递到同一视图上的另一个视图控制器

通过单击 Swift 3 中的 Alert Ok 按钮,翻转回同一视图控制器上的另一个视图

Python:从项目层次结构中同一级别的另一个目录导入模块