Bộ điều khiển AngularJS có thể kế thừa từ bộ điều khiển khác trong cùng một mô-đun không?


198

Trong một mô-đun, bộ điều khiển có thể kế thừa các thuộc tính từ bộ điều khiển bên ngoài:

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

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

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

Ví dụ qua: Liên kết chết : http://blog.omkarpatil.com/2013/02/controll-inherribution-in-angularjs.html

Cũng có thể một bộ điều khiển bên trong một mô-đun thừa kế từ anh chị em?

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

Mã thứ hai không hoạt động vì $injector.invokeyêu cầu một hàm là tham số đầu tiên và không tìm thấy tham chiếu đến ParentCtrl.



2
sang một bên: điều này không giống như thừa kế, mà giống như phương pháp chia sẻ hoặc tiêm chích. Có lẽ chỉ là ngữ nghĩa.
alockwood05

Liên kết cho ví dụ không còn hợp lệ nữa.
AlexS

Liên kết bộ nhớ cache của Google: webcache.googleusercontent.com/ từ đó chỉ ra Fiddle thú vị này: jsfiddle.net/mhevery/u6s88/12
Federico Elles

Câu trả lời:


289

Có, nhưng bạn có thể sử dụng $controllerdịch vụ để khởi tạo bộ điều khiển thay thế: -

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

ParentCtrlnên là một controllerhoặc có thể sử dụng một service?
Gontard

@gontard: Trong trường hợp này phải là bộ điều khiển, vì $controllerchỉ có thể sử dụng bộ điều khiển đã đăng ký.
ZeissS

10
Đó là một giải pháp rất tốt. Cảm ơn bạn. Nhưng làm thế nào tôi có thể làm điều đó trong trường hợp tôi đang sử dụng cú pháp Trình điều khiển dưới dạng?
Tới Ka

1
Các fiddle trên đã được hỏi như một câu hỏi. Điều đáng chú ý là bộ điều khiển Chỉ đơn giản gán bộ điều khiển cho phạm vi - Vì vậy, bạn sẽ thay đổi $scopethành this(về lý thuyết)
Dan Pantry

4
Điều này làm việc cho tôi, tuy nhiên tôi đang cố gắng làm điều này theo cách mà tôi có bộ điều khiển chính và bộ điều khiển con trên cùng một trang. Điều này khiến hoạt động $ http trong bộ điều khiển chính chạy hai lần. Khi bộ điều khiển con tiêm phạm vi của bộ điều khiển cha, mảng $ scope.AllMembers của tôi được điền hai lần vì bộ điều khiển chính làm cho nó chạy, sau đó bộ điều khiển con làm cho nó chạy lại. Có cách nào để ngăn chặn điều đó?
Ryan Mann

20

Trong trường hợp bạn đang sử dụng vmcú pháp của bộ điều khiển , đây là giải pháp của tôi:

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

Thật không may, bạn không thể sử dụng $controller.call(vm, 'BaseGenericCtrl'...), để chuyển ngữ cảnh hiện tại vào hàm đóng (for reload()), do đó chỉ có một giải pháp là sử dụng thisbên trong hàm được kế thừa để thay đổi động bối cảnh.


Bạn không thể làm điều này thay vào đó? > bộ điều khiển $ ('BaseGenericControl', {vm: vm});
herringtown

vmchỉ là một biến trong bộ điều khiển, tôi không nghĩ vậy nên Angular có thể sử dụng nó như mong đợi.
IPropetFactory

8

Tôi nghĩ, bạn nên sử dụng nhà máy hoặc dịch vụ, để cung cấp các chức năng hoặc dữ liệu có thể truy cập cho cả hai bộ điều khiển.

đây là câu hỏi tương tự ---> kế thừa bộ điều khiển AngularJS


Vâng, đó là một cách, cảm ơn. Tôi đã xem qua bài đăng đó khi tôi đang tìm kiếm giải pháp. Tôi đã suy nghĩ nếu có một số cách để tải chức năng điều khiển và mở rộng "cái này" với nó.
Tới Ka

Tôi muốn có một biến phổ quát loadingđể khi tải dữ liệu tôi luôn làm điều tương tự, tôi không nghĩ các nhà máy có thể làm điều đó. Bộ điều khiển chính của tôi có thể có một biến tải nhưng nhà máy không thể thao tác nó ... đúng không?!
PixMach

7

Để trả lời cho vấn đề được đưa ra trong câu trả lời này của gmontague , tôi đã tìm ra một phương thức để kế thừa bộ điều khiển bằng cách sử dụng $ controller () và vẫn sử dụng cú pháp "as" của bộ điều khiển.

Đầu tiên, sử dụng cú pháp "as" khi bạn kế thừa cách gọi $ controller ():

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

    });

Sau đó, trong mẫu HTML, nếu thuộc tính được xác định bởi cha mẹ, thì sử dụng parent.để truy xuất các thuộc tính được kế thừa từ cha mẹ; nếu được xác định bởi con, sau đó sử dụng child.để lấy nó.

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

5

Vâng, tôi đã làm điều này theo một cách khác. Trong trường hợp của tôi, tôi muốn một hàm áp dụng các chức năng và thuộc tính tương tự trong các bộ điều khiển khác. Tôi thích nó, ngoại trừ bởi các tham số. Theo cách này, tất cả các ChildCtrl của bạn cần nhận vị trí $.

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

Đối với những người thắc mắc, bạn có thể mở rộng bộ điều khiển thành phần theo cùng một cách, sử dụng phương pháp trong câu trả lời được chấp nhận.

Sử dụng phương pháp sau:

Thành phần cha mẹ (để mở rộng từ):

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

Thành phần con (phần mở rộng):

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

Bí quyết là sử dụng các bộ điều khiển có tên, thay vì định nghĩa chúng trong định nghĩa thành phần.


2

Như đã đề cập trong câu trả lời được chấp nhận, bạn có thể "kế thừa" các sửa đổi của bộ điều khiển chính thành $ scope và các dịch vụ khác bằng cách gọi: $controller('ParentCtrl', {$scope: $scope, etc: etc});trong bộ điều khiển con của bạn.

Tuy nhiên , điều này không thành công nếu bạn đã quen với việc sử dụng cú pháp 'as' của bộ điều khiển, ví dụ như trong

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

Nếu foođược đặt trong bộ điều khiển chính (thông qua this.foo = ...), bộ điều khiển con sẽ không có quyền truy cập vào nó.

Như đã đề cập trong các bình luận, bạn có thể gán kết quả của trình điều khiển $ trực tiếp cho phạm vi:

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

Lưu ý: Sau đó, bạn phải xóa phần 'dưới dạng' ng-controller=vì bạn chỉ định tên đối tượng trong mã và không còn là mẫu.


Sử dụng cú pháp "control as" không có vấn đề gì. Xem câu trả lời của tôi: stackoverflow.com/a/36549465/2197555
gm2008

2

Tôi đã sử dụng cú pháp "Trình điều khiển dưới dạng" với vm = thisvà muốn kế thừa bộ điều khiển. Tôi gặp vấn đề nếu bộ điều khiển cha mẹ có chức năng sửa đổi một biến.

Sử dụng câu trả lời của IPro FlagFactorySalman Abbas , tôi đã làm như sau để có quyền truy cập vào các biến cha:

(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

Bạn có thể sử dụng một cơ chế kế thừa JavaScript đơn giản. Cũng đừng quên vượt qua một dịch vụ góc cần thiết để gọi phương thức .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);

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.