คอนโทรลเลอร์ 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});
});

ตัวอย่างผ่าน: Dead link : 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.invokeParentCtrl


สิ่งนี้น่าจะช่วยได้: stackoverflow.com/questions/16828287/…
บาร์ต

2
กัน: สิ่งนี้ดูไม่เหมือนการรับมรดก แต่เพิ่มเติมเช่นวิธีการแชร์หรือการฉีด บางทีอาจเป็นแค่ความหมาย
alockwood05

ลิงก์สำหรับตัวอย่างไม่ถูกต้องอีกต่อไป
AlexS

ลิงก์ Google Cache: webcache.googleusercontent.com/ซึ่งชี้ไปที่ซอที่น่าสนใจนี้: jsfiddle.net/mhevery/u6s88/12
Federico Elles

คำตอบ:


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?
gontard

@gontard: ในกรณีนี้จะต้องเป็นคอนโทรลเลอร์เนื่องจาก$controllerสามารถใช้คอนโทรลเลอร์ที่ลงทะเบียนได้เท่านั้น
ZeissS

10
มันเป็นทางออกที่ดีมาก ขอบคุณ. แต่ฉันจะทำอย่างไรในกรณีที่ฉันใช้ตัวควบคุมเป็นไวยากรณ์?
ถึง Ka

1
ซอถูกถามว่าเป็นคำถาม เป็นที่น่าสังเกตว่าคอนโทรลเลอร์เพียงแค่กำหนดคอนโทรลเลอร์ให้กับขอบเขต - ดังนั้นคุณจะเปลี่ยน$scopeเป็นthis(ในทางทฤษฎี)
Dan Pantry

4
สิ่งนี้ใช้ได้สำหรับฉัน แต่ฉันพยายามทำสิ่งนี้ในแบบที่ฉันมีตัวควบคุมหลักและตัวควบคุมลูกในหน้าเดียวกัน ซึ่งทำให้การดำเนินการ $ http ในตัวควบคุมหลักให้ทำงานสองครั้ง เมื่อตัวควบคุมลูกแทรกขอบเขตของตัวควบคุมหลักของฉัน $ scope อาร์เรย์ AllMembers ของฉันได้รับการบรรจุสองครั้งเนื่องจากตัวควบคุมหลักทำให้มันทำงานแล้วตัวควบคุมลูกจะทำให้มันทำงานอีกครั้ง มีวิธีป้องกันไหม
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'...)เพื่อส่งบริบทปัจจุบันไปสู่reload()ฟังก์ชั่นปิด (สำหรับ) ดังนั้นจึงมีทางออกเดียวที่จะใช้thisภายในฟังก์ชันที่สืบทอดมาเพื่อเปลี่ยนบริบทแบบไดนามิก


คุณไม่สามารถทำสิ่งนี้แทนได้หรือ > $ controller ('BaseGenericControl', {vm: vm});
herringtown

vmเป็นเพียงตัวแปรภายในคอนโทรลเลอร์ฉันไม่คิดว่า Angular สามารถใช้งานได้ตามที่คาดไว้
IProblemFactory

8

ฉันคิดว่าคุณควรใช้โรงงานหรือบริการเพื่อให้ฟังก์ชั่นหรือข้อมูลที่เข้าถึงได้สำหรับตัวควบคุมทั้งสอง

นี่คือคำถามที่คล้ายกัน ---> การสืบทอดตัวควบคุม AngularJS


ใช่นั่นเป็นวิธีหนึ่งขอบคุณ ฉันเจอโพสต์นั้นเมื่อฉันค้นหาวิธีแก้ปัญหา ฉันคิดว่ามีวิธีโหลดฟังก์ชั่นคอนโทรลเลอร์และขยาย "นี่" ด้วย
ถึง Ka

ฉันต้องการloadingตัวแปรสากลเพื่อที่ว่าเมื่อมีการโหลดข้อมูลฉันจะทำสิ่งเดียวกันเสมอฉันไม่คิดว่าโรงงานสามารถทำเช่นนั้นได้ ตัวควบคุมหลักของฉันสามารถมีตัวแปรในการโหลดได้ แต่โรงงานไม่สามารถจัดการได้ ... ใช่ไหม?!
PixMach

7

ในการตอบสนองต่อปัญหาที่เกิดขึ้นในคำตอบนี้โดย gmontagueฉันได้พบวิธีการสืบทอดคอนโทรลเลอร์โดยใช้ $ controller () และยังคงใช้คอนโทรลเลอร์ "เป็น" ไวยากรณ์

ประการแรกใช้ไวยากรณ์ "เป็น" เมื่อคุณสืบทอดการเรียก $ 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);

    });

จากนั้นในเทมเพลต HTML หากคุณสมบัติถูกกำหนดโดยพาเรนต์ให้ใช้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();
});

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

ตามที่ระบุไว้ในคำตอบที่ยอมรับคุณสามารถ "สืบทอด" การแก้ไขตัวควบคุมหลักของ $ scope และบริการอื่น ๆ โดยการโทร: $controller('ParentCtrl', {$scope: $scope, etc: etc});ในตัวควบคุมลูกของคุณ

อย่างไรก็ตามสิ่งนี้จะล้มเหลวหากคุณคุ้นเคยกับการใช้ตัวควบคุม 'เป็น' ไวยากรณ์เช่นใน

<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

2

ฉันใช้ไวยากรณ์ "Controller as" กับ vm = thisและต้องการสืบทอดคอนโทรลเลอร์ ฉันมีปัญหาถ้าตัวควบคุมหลักของฉันมีฟังก์ชันที่แก้ไขตัวแปร

การใช้คำตอบของ IProblemFactoryและSalman 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 อย่างง่าย อย่าลืมให้บริการเชิงมุมที่จำเป็นเพื่อเรียกใช้วิธีการโทร

//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.