2013-08-27 12 views
188

obrębie modułu, sterownik może dziedziczyć właściwości z zewnętrznego kontrolera:Czy kontroler AngularJS może być dziedziczony z innego kontrolera w tym samym module?

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

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

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

przykład przez: martwy link: http://blog.omkarpatil.com/2013/02/controller-inheritance-in-angularjs.html

Może również kontroler wewnątrz modułu dziedziczą z rodzeństwem?

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

Drugi kod nie działa od $injector.invoke wymaga funkcję jako pierwszy parametr i nie znaleźć odniesienie do ParentCtrl.

+0

To powinno pomóc: http://stackoverflow.com/questions/16828287/what-things-can-be-injected-into-others-in-angular-js – Bart

+2

bok: to nie robi wygląda jak dziedziczenie, ale bardziej przypomina metody dzielenia się lub wstrzykiwanie. Być może po prostu semantyka. – alockwood05

+0

Link do tego przykładu jest już nieważny. – AlexS

Odpowiedz

271

Tak, to możliwe, ale trzeba korzystać z usługi $controller instancji kontrolera zamiast: -

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

'ParentCtrl' powinien być' kontrolerem' lub czy można użyć 'usługi'? – gontard

+0

@gontard: W tym przypadku musi to być kontroler, ponieważ 'kontroler $' może używać tylko zarejestrowanych kontrolerów. – ZeissS

+10

To bardzo dobre rozwiązanie. Dziękuję Ci. Ale w jaki sposób mogę to zrobić w przypadku, gdy używam składni Controller As? –

6

Myślę, że powinieneś użyć fabryki lub usługi, aby udostępnić dostępne funkcje lub dane dla obu kontrolerów.

tutaj jest podobne pytanie --->AngularJS controller inheritance

+0

Tak, to jest jeden sposób, dzięki. Natknąłem się na to stanowisko, gdy szukałem rozwiązania. Zastanawiałem się, czy istnieje jakiś sposób na załadowanie funkcji kontrolera i rozszerzenie tego "z". –

+0

Chciałbym mieć uniwersalną zmienną 'loading', więc gdy ładuję dane, zawsze robię to samo, nie sądzę, że fabryki to potrafią. Mój kontroler nadrzędny może mieć zmienną ładującą, ale fabryka nie może nią manipulować ... prawda ?! – PixMach

2

Jak wspomniano w przyjętym odpowiedź możesz "dziedziczyć" modyfikacje kontrolera nadrzędnego w zakresie $ scope i innych usługach, wywołując: $controller('ParentCtrl', {$scope: $scope, etc: etc}); w kontrolce podrzędnej.

Jednak, to się nie powiedzie, jeśli jesteś przyzwyczajony do korzystania z kontrolera „jako” składni, przykładowo w

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

Jeśli foo został ustawiony w sterowniku dominującej (poprzez this.foo = ...), kontroler dziecko nie mieć do niego dostępu.

Jak wspomniano w komentarzach można przypisać wynik $ kontrolera bezpośrednio z zakresem:

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

Uwaga: Następnie musi usunąć „jako” częściowo z ng-controller=, ponieważ jesteś określający nazwa instancji w kodzie, a nie szablon.

+0

Używanie składni "controller as" nie stanowi problemu. Zobacz moją odpowiedź: http://stackoverflow.com/a/36549465/2197555 – gm2008

3

Cóż, zrobiłem to w inny sposób. W moim przypadku chciałem funkcji, która stosuje te same funkcje i właściwości w innych kontrolerach. Podobało mi się to, z wyjątkiem parametrów. W ten sposób wszystkie Twoje ChildCtrls muszą otrzymać lokalizację $.

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

W przypadku korzystania vm controller syntax, tu jest moje rozwiązanie:

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

Niestety, nie można używać $controller.call(vm, 'BaseGenericCtrl'...), aby przejść do aktualnego kontekstu zamknięcia (na reload()) funkcji, stąd tylko jeden rozwiązaniem jest użycie this w odziedziczonej funkcji, aby dynamicznie zmieniać kontekst.

+0

Nie możesz tego właśnie zrobić zamiast tego? > kontroler $ ("BaseGenericControl", {vm: vm}); – herringtown

+0

'vm' jest po prostu zmienną wewnątrz kontrolera, nie sądzę, żeby Angular mógł go użyć zgodnie z oczekiwaniami. – IProblemFactory

6

W odpowiedzi na problem poruszony w this answer by gmontague, znalazłem metodę dziedziczenia kontrolera za pomocą kontrolera $() i nadal używam składni "as" kontrolera.

Po pierwsze, należy użyć „jako” składni kiedy dziedziczą wywołanie $ kontrolera():

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

    }); 

Następnie w szablonie HTML, jeżeli nieruchomość jest zdefiniowany przez rodzica, a następnie użyć parent. odzyskać właściwości dziedziczone od rodziców ; jeśli zdefiniowane przez dziecko, użyj child., aby je odzyskać.

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

używałem „Kontroler jako” składni z vm = this i chciał odziedziczyć kontrolera. Miałem problemy, jeśli mój kontroler nadrzędny miał funkcję, która zmodyfikowała zmienną.

Korzystanie IProblemFactory's i Salman Abbas's odpowiedź, zrobiłem następujące czynności, aby mieć dostęp do zmiennych nadrzędny:

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

2

Dla tych, zastanawiając się, można rozszerzyć kontrolery składowych w taki sam sposób, przy użyciu metoda w zaakceptowanej odpowiedzi.

użyć podejścia:

element nadrzędny (rozszerzenie z):

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

składnik dziecko (jedno rozszerzenie)

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

Trick jest użycie nazwie kontrolerów , zamiast definiować je w definicji komponentu.

0

Możesz użyć prostego mechanizmu dziedziczenia JavaScript. Nie zapominaj również o przekazywaniu potrzebnych usług kanciastych w celu wywołania metody .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); 
Powiązane problemy