问题
I'm having a hard time trying to understand the best way to achieve inheritance with my controllers. I've seen a few other posts here about these but I still don´t get some things.
Here's what I have:
- 2 controllers which are 80% similar. I already have a factory which both use to get the data which will be displayed.
- I use the controllerAs notation, with var vm = this
- there's a mix of vars and functions which will be used in the view and therefore are created inside vm, and some other internal vars and functions which are not.
- so I tried to create a single parent controller with all this and then use injection to create these 2 controllers, overwriting only what I need, but this is not working as I expected and also I'm not sure this is the right thing to do
Here is a simplified version of the code.
(function() {
angular
.controller('ParentController', ParentController)
ParentController.$inject = [ '$scope', '$location' ];
function ParentController($scope, $location) {
var vm = this; // view model
var URL = $location.url();
var isDataLoaded = false;
vm.predicate = 'order';
vm.reverse = false;
vm.results;
vm.isDataReady = isDataReady;
vm.setOrder = setOrder;
function isDataReady() {
return isDataLoaded;
}
function setOrder(p) {
vm.reverse = (p === vm.predicate) ? !vm.reverse : false;
vm.predicate = p;
}
$scope.$on('READ.FINISHED', function() {
isDataLoaded = true;
})
}
})();
-
(function() {
angular
.controller('ChildController', ChildController)
ChildController.$inject = ['$controller', '$scope', 'myFactory'];
function ChildController($controller, $scope, myFactory) {
$controller('ParentController', {$scope: $scope});
var TEMPLATE = 'SCREEN';
// ************** M A I N **************
myFactory.getResults(URL, vm);
}
})();
This is now working as I expected.
When I inject ChildController with ParentController, do I really need to inject the $scope? I'm actually using vm. Also, do I need to inject also $location? In this example when I execute my code I'm forced to use var URL = $location.url(); again in my ChildController, I expected to inherite the value from ParentController.
So the thing is, am I only getting values from $scope if I work like this? what about vm? and what about those vars/functions declared outside vm like var isDataLoaded?
I'd appreciate some insight about this. Would this be the right way to do it?
Many thanks.
EDIT: Ok, I found out how to use my ControllerAs syntax with this. Code in the child controller would be like this:
function ChildController($controller, $scope, myFactory) {
$controller('ParentController as vm', {$scope: $scope});
var vm = $scope.vm;
var TEMPLATE = 'SCREEN';
// ************** M A I N **************
myFactory.getResults(URL, vm);
}
But I still need to get a way to also recover the regular var/functions inside the parent controller. Any ideas? Can it be done cleanly?
回答1:
I recommend you to implement usual javascript's inheritance mechanism between two classes. At ChildController constructor you will execute ParentController constructor and pass to it injected parameters (without using $controller service).
I created simple example (very far from your logic but consider it as patten). I have two controllers: ParentController and ChildController, that inherited from first one. I used only ChildController with $scope, $interval and custom service with name "myservice" (all of them needed only for example). You can see that I used methods and fields from parent and child controllers. Logic of my app is very simple: you can add new items to collection (by means of ParentController) and remove them(by means of ChildController) with logging.
At that case I recommend you use ChildController as ctrl for Data Binding instead of $scope, because it more in line inheritance paradigm (we inherite controllers(ctrl) not $scope).
P.S. If you be going to use inheritance very often I recommend you to use TypeScript - it gives very simple and flexible solution of this problem in c# style.
Controllers.js
function ParentController(myservice, $scope, $interval) {
this.newOne={};
this.lastacivity={};
this.$interval = $interval;
this.items = myservice();
}
ParentController.prototype.Log = function(item){
var self = this;
this.$interval(function(){
console.log(item);
self.lastacivity = item;
}, 100, 1);
}
ParentController.prototype.AddNew = function (Name, Age) {
var newitem = {
name: this.newOne.Name,
age: this.newOne.Age
}
this.items.push(newitem);
this.Log(newitem);
}
function ChildController(myservice, $scope, $interval) {
//Transfering myservice, $scope, $interval from ChildController to ParentController constructor,
//also you can pass all what you want: $http, $location etc.
ParentController.apply(this, [myservice, $scope, $interval]);
}
ChildController.prototype = Object.create(ParentController.prototype)
//your ChildController's own methods
ChildController.prototype.Remove = function (item) {
this.items.splice(this.items.indexOf(item), 1);
this.Log(item);
}
script.js
(function(angular) {
'use strict';
angular.module('scopeExample', []).factory('myservice', function() {
var items = [
{name:"Mike",age:21},
{name:"Kate",age:22},
{name:"Tom",age:11}
];
return function(){
return items;
}
}).controller('ChildController', ['myservice', '$scope', '$interval', ChildController]);
})(window.angular);
HTML
<body ng-app="scopeExample">
<div ng-controller="ChildController as ctrl">
<label>Name</label>
<input type='text' ng-model='ctrl.newOne.Name'/>
<label>Age</label>
<input type='text' ng-model='ctrl.newOne.Age'/>
<input type='button' value='add' ng-click='ctrl.AddNew()' ng-disabled="!(ctrl.newOne.Name && ctrl.newOne.Age)"/>
<br>
<br>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in ctrl.items">
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td>
<input type='button' value='X' ng-click='ctrl.Remove(item)'/>
</td>
</tr>
</tbody>
</table>
<p>{{ctrl.lastacivity | json}}</p>
</div>
</body>
来源:https://stackoverflow.com/questions/35032088/controller-inheritance-with-injection