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最初のパラメータとして関数が必要であり、への参照が見つからないため、2番目のコードは機能しませんParentCtrl



2
余談ですが、これは継承のようには見えませんが、メソッドの共有や注入のようです。おそらく単なるセマンティクス。
alockwood05 2015年

例のリンクはもう有効ではありません。
AlexS

Googleキャッシュリンク:webcache.googleusercontent.com/…これは、この興味深いフィドルを指す:jsfiddle.net/mhevery/u6s88/12
フェデリコエレス

回答:


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でなければなりませんかcontrollerserviceそれとも使用できますか?
gontard 2014年

@gontard:この場合、$controller登録されたコントローラーのみを使用できるため、コントローラーである必要があります。
ZeissS 2014年

10
これは非常に優れたソリューションです。ありがとうございました。しかし、Controller As構文を使用している場合はどうすればよいですか?
へのKa

1
上記のフィドルは質問として尋ねられました。ControllerAsがスコープにコントローラを割り当てるだけであることは注目に値します-したがって$scopethis(理論的には)に変更されます
Dan Pantry

4
これは私にとってはうまくいきましたが、私は同じページに親コントローラーと子コントローラーがあるようにこれをやろうとしています。これにより、親コントローラーの$ http操作が2回実行されます。子コントローラーが親コントローラーのスコープを挿入すると、$ scope.AllMembers配列は、親コントローラーが実行するように2倍に設定され、その後、子コントローラーが再び実行します。それを防ぐ方法はありますか?
Ryan Mann

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ため、動的にコンテキストを変更するために継承された関数内で使用するのは1つの解決策だけです。


代わりにこれを行ったのではないですか?> $ controller( 'BaseGenericControl'、{vm:vm});
herringtown

vmコントローラ内の変数なので、Angularが期待どおりに使用できるとは思いません。
IProblemFactory 2016

8

両方のコントローラーにアクセス可能な機能またはデータを提供するには、ファクトリーまたはサービスを使用する必要があると思います。

これは同様の質問です---> AngularJSコントローラーの継承


はい、それは片道です、ありがとう。解決策を探していたときにその投稿に出くわしました。コントローラーの機能を読み込んで「これ」を拡張する方法があるかどうか考えていました。
へのKa

ユニバーサルloading変数を用意して、データの読み込み中に常に同じことをしたいのですが、工場ではそれができないと思います。私の親コントローラーは読み込み変数を持つことができますが、ファクトリーはそれを操作することはできません...?
PixMach

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.して、親から継承したプロパティを取得します。子によって定義されている場合は、それ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;
});

注:次に「から」の部分を削除する必要がありますng-controller=しますが、コード、およびもはやテンプレートのインスタンス名を指定しているので、。


"controller as"構文を使用しても問題ありません。私の答えをご覧ください:stackoverflow.com/a/36549465/2197555
gm2008

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.