Dapatkah pengontrol AngularJS mewarisi dari pengontrol lain dalam modul yang sama?

198

Di dalam modul, pengontrol dapat mewarisi properti dari pengontrol luar:

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

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

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

Contoh melalui: Tautan mati : http://blog.omkarpatil.com/2013/02/controller-inheritance-in-angularjs.html

Bisakah juga kontroler di dalam modul yang diwarisi dari saudara kandung?

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

Kode kedua tidak berfungsi karena $injector.invokememerlukan fungsi sebagai parameter pertama dan tidak menemukan referensi ParentCtrl.

Federico Elles
sumber
Ini seharusnya membantu: stackoverflow.com/questions/16828287/…
Bart
2
samping: ini tidak terlihat seperti warisan, tetapi lebih seperti metode berbagi atau suntikan. Mungkin hanya semantik.
alockwood05
Tautan untuk contoh tidak valid lagi.
AlexS
Google Cache Link: webcache.googleusercontent.com/... yang menunjuk ke Fiddle yang menarik ini: jsfiddle.net/mhevery/u6s88/12
Federico Elles

Jawaban:

289

Ya, itu bisa tetapi Anda harus menggunakan $controllerlayanan untuk instantiate controller sebagai gantinya: -

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
});
Salman von Abbas
sumber
ParentCtrlharus menjadi controlleratau mungkin menggunakan service?
gontard
@ontard: Dalam hal ini harus berupa controller, karena $controllerhanya dapat menggunakan pengontrol terdaftar.
ZeissS
10
Ini adalah solusi yang sangat bagus. Terima kasih. Tetapi bagaimana saya melakukannya jika saya menggunakan sintaks Controller As?
Kepada
1
Biola di atas diajukan sebagai pertanyaan. Perlu dicatat bahwa controllerAs hanya menetapkan controller ke ruang lingkup - Jadi Anda akan berubah $scopemenjadi this(secara teori)
Dan Pantry
4
Ini bekerja untuk saya, namun saya mencoba melakukan ini dengan cara yang saya punya pengendali induk dan pengendali anak pada halaman yang sama. Ini menyebabkan operasi $ http pada pengontrol induk berjalan dua kali. Ketika pengendali anak menyuntikkan lingkup pengendali induk, array $ scope.AllMembers saya akan dipopulasikan dua kali karena pengendali induk menyebabkannya berjalan, kemudian pengendali anak menyebabkannya berjalan lagi. Apakah ada cara untuk mencegahnya?
Ryan Mann
20

Jika Anda menggunakan vmsintaks controller , berikut ini solusinya:

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

Sayangnya, Anda tidak dapat menggunakan $controller.call(vm, 'BaseGenericCtrl'...), untuk mengirimkan konteks saat ini ke reload()fungsi closure (for ), karenanya hanya satu solusi yang digunakan thisdi dalam fungsi yang diwarisi untuk mengubah konteks secara dinamis.

IProblemFactory
sumber
Tidak bisakah Anda melakukan ini saja? > $ controller ('BaseGenericControl', {vm: vm});
herringtown
vmhanyalah sebuah variabel di dalam kontroler, saya tidak berpikir begitu Angular dapat menggunakannya seperti yang diharapkan.
IProblemFactory
8

Saya pikir, Anda harus menggunakan pabrik atau layanan, untuk memberikan fungsi atau data yang dapat diakses untuk kedua pengendali.

di sini adalah pertanyaan serupa ---> warisan pengontrol AngularJS

LauroSkr
sumber
Ya itu satu arah, terima kasih. Saya menemukan posting itu ketika saya mencari solusi. Saya berpikir jika ada beberapa cara untuk memuat fungsi pengontrol dan memperpanjang "ini" dengannya.
Kepada
Saya ingin memiliki loadingvariabel universal sehingga ketika data dimuat saya selalu melakukan hal yang sama, saya tidak berpikir pabrik bisa melakukan itu. Pengontrol orang tua saya dapat memiliki variabel pemuatan tetapi pabrik tidak dapat memanipulasinya ... kan ?!
PixMach
7

Menanggapi masalah yang diangkat dalam jawaban ini oleh gmontague , saya telah menemukan metode untuk mewarisi pengontrol menggunakan $ controller (), dan masih menggunakan sintaks controller "as".

Pertama, gunakan sintaks "sebagai" saat Anda mewarisi memanggil $ 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);

    });

Kemudian, dalam templat HTML, jika properti ditentukan oleh induk, lalu gunakan parent.untuk mengambil properti yang diwarisi dari induk; jika ditentukan oleh child, maka gunakan child.untuk mengambilnya.

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

Yah, saya melakukan ini dengan cara lain. Dalam kasus saya, saya ingin fungsi yang menerapkan fungsi dan properti yang sama di pengontrol lain. Saya menyukainya, kecuali berdasarkan parameter. Dengan cara ini, semua ChildCtrls Anda perlu menerima $ lokasi.

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();
});
Fabio Montefuscolo
sumber
4

Bagi mereka yang bertanya-tanya, Anda dapat memperluas pengontrol komponen dengan cara yang sama, menggunakan metode dalam jawaban yang diterima.

Gunakan pendekatan berikut:

Komponen induk (untuk memperpanjang dari):

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

Komponen anak (yang membentang):

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

Caranya adalah dengan menggunakan pengendali bernama, daripada mendefinisikan mereka dalam definisi komponen.

Adam Reis
sumber
2

Seperti disebutkan dalam jawaban yang diterima, Anda dapat "mewarisi" modifikasi pengendali orang tua ke $ scope dan layanan lain dengan menelepon: $controller('ParentCtrl', {$scope: $scope, etc: etc});di pengendali anak Anda.

Namun , ini gagal jika Anda terbiasa menggunakan sintaks 'as' controller, misalnya dalam

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

Jika foodiatur di pengontrol induk (via this.foo = ...), pengontrol anak tidak akan memiliki akses ke sana.

Seperti disebutkan dalam komentar, Anda dapat menetapkan hasil $ controller langsung ke lingkup:

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

Catatan: Anda kemudian harus menghapus bagian 'as' dari ng-controller=, karena Anda menentukan nama instance dalam kode, dan tidak lagi template.

gmontague
sumber
Menggunakan sintaks "controller as" tidak memiliki masalah. Lihat jawaban saya: stackoverflow.com/a/36549465/2197555
gm2008
2

Saya menggunakan sintaks "Controller as" dengan vm = thisdan ingin mewarisi sebuah controller. Saya punya masalah jika controller orangtua saya memiliki fungsi yang mengubah variabel.

Menggunakan jawaban IProblemFactory dan Salman Abbas , saya melakukan hal berikut untuk memiliki akses ke variabel induk:

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

dufaux
sumber
0

Anda dapat menggunakan mekanisme pewarisan JavaScript sederhana. Juga jangan lupa berikan layanan sudut yang perlu untuk memanggil metode .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);
trueboroda
sumber