ฉันมีสามคอนโทรลเลอร์ที่ค่อนข้างคล้ายกัน ฉันต้องการมีคอนโทรลเลอร์ซึ่งทั้งสามตัวนี้ขยายและแบ่งปันฟังก์ชั่นของมัน
ฉันมีสามคอนโทรลเลอร์ที่ค่อนข้างคล้ายกัน ฉันต้องการมีคอนโทรลเลอร์ซึ่งทั้งสามตัวนี้ขยายและแบ่งปันฟังก์ชั่นของมัน
คำตอบ:
บางทีคุณอาจไม่ขยายคอนโทรลเลอร์ แต่ก็เป็นไปได้ที่จะขยายคอนโทรลเลอร์หรือทำให้คอนโทรลเลอร์เดี่ยวเป็น mixin ของคอนโทรลเลอร์หลายตัว
module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) {
// Initialize the super class and extend it.
angular.extend(this, $controller('CtrlImpl', {$scope: $scope}));
… Additional extensions to create a mixin.
}]);
เมื่อตัวควบคุมหลักถูกสร้างขึ้นตรรกะที่อยู่ภายในมันจะถูกดำเนินการ ดู $ controller () สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ แต่$scope
จะต้องส่งผ่านค่าเท่านั้น ค่าอื่น ๆ ทั้งหมดจะถูกฉีดตามปกติ
@mwarrenความกังวลของคุณได้รับการดูแลโดยอัตโนมัติอย่างน่าอัศจรรย์โดยการฉีดพึ่งพาเชิงมุม สิ่งที่คุณต้องใช้คือการฉีดขอบเขต $ ถึงแม้ว่าคุณสามารถแทนที่ค่าที่ฉีดอื่น ๆ ได้หากต้องการ นำตัวอย่างต่อไปนี้:
(function(angular) {
var module = angular.module('stackoverflow.example',[]);
module.controller('simpleController', function($scope, $document) {
this.getOrigin = function() {
return $document[0].location.origin;
};
});
module.controller('complexController', function($scope, $controller) {
angular.extend(this, $controller('simpleController', {$scope: $scope}));
});
})(angular);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.js"></script>
<div ng-app="stackoverflow.example">
<div ng-controller="complexController as C">
<span><b>Origin from Controller:</b> {{C.getOrigin()}}</span>
</div>
</div>
แม้ว่าเอกสาร $ จะไม่ถูกส่งผ่านไปยัง 'simpleController' เมื่อมันถูกสร้างขึ้นโดย 'documentController' $ document จะถูกฉีดให้เรา
$.extend()
นั้นคุณสามารถโทรหา$controller('CtrlImpl', {$scope: $scope});
angular.extend
(หรือ$.extend
) จริง ๆ แล้วหมายถึงการขยาย$scope
เท่านั้น แต่ถ้าตัวควบคุมฐานของคุณยังกำหนดคุณสมบัติบางอย่าง (เช่นthis.myVar=5
) คุณจะสามารถเข้าถึงได้this.myVar
ในการควบคุมการขยายเมื่อใช้angular.extend
handleSubmitClick
ที่จะเรียกhandleLogin
ซึ่งในทางกลับกันมีและloginSuccess
loginFail
ดังนั้นในการควบคุมการขยายของฉันฉันแล้วก็ต้องเกินhandleSubmitClick
, handleLogin
และloginSucess
สำหรับการที่ถูกต้องloginSuccess
ฟังก์ชั่นที่จะใช้
สำหรับการสืบทอดคุณสามารถใช้รูปแบบการสืบทอด JavaScript มาตรฐาน นี่คือตัวอย่างที่ใช้$injector
function Parent($scope) {
$scope.name = 'Human';
$scope.clickParent = function() {
$scope.name = 'Clicked from base controller';
}
}
function Child($scope, $injector) {
$injector.invoke(Parent, this, {$scope: $scope});
$scope.name = 'Human Child';
$scope.clickChild = function(){
$scope.clickParent();
}
}
Child.prototype = Object.create(Parent.prototype);
ในกรณีที่คุณใช้controllerAs
ไวยากรณ์ (ซึ่งฉันขอแนะนำอย่างยิ่ง) มันจะง่ายยิ่งขึ้นในการใช้รูปแบบการสืบทอดดั้งเดิม:
function BaseCtrl() {
this.name = 'foobar';
}
BaseCtrl.prototype.parentMethod = function () {
//body
};
function ChildCtrl() {
BaseCtrl.call(this);
this.name = 'baz';
}
ChildCtrl.prototype = Object.create(BaseCtrl.prototype);
ChildCtrl.prototype.childMethod = function () {
this.parentMethod();
//body
};
app.controller('BaseCtrl', BaseCtrl);
app.controller('ChildCtrl', ChildCtrl);
อีกวิธีหนึ่งก็คือการสร้างฟังก์ชั่น "นามธรรม" ซึ่งจะเป็นตัวควบคุมฐานของคุณ:
function BaseController() {
this.click = function () {
//some actions here
};
}
module.controller('ChildCtrl', ['$scope', function ($scope) {
BaseController.call($scope);
$scope.anotherClick = function () {
//other actions
};
}]);
ดีฉันไม่แน่ใจว่าสิ่งที่คุณต้องการที่จะประสบความสำเร็จ แต่บริการมักจะเป็นวิธีที่จะไป คุณยังสามารถใช้คุณสมบัติการสืบทอดขอบเขตของ Angular เพื่อแบ่งปันรหัสระหว่างคอนโทรลเลอร์:
<body ng-controller="ParentCtrl">
<div ng-controller="FirstChildCtrl"></div>
<div ng-controller="SecondChildCtrl"></div>
</body>
function ParentCtrl($scope) {
$scope.fx = function() {
alert("Hello World");
});
}
function FirstChildCtrl($scope) {
// $scope.fx() is available here
}
function SecondChildCtrl($scope) {
// $scope.fx() is available here
}
$scope.$parent.fx( )
เป็นวิธีที่สะอาดกว่าที่จะทำเพราะนั่นคือสิ่งที่มันถูกกำหนดจริง?
คุณไม่ขยายคอนโทรลเลอร์ หากพวกเขาทำหน้าที่ขั้นพื้นฐานเดียวกันฟังก์ชั่นเหล่านั้นจะต้องถูกย้ายไปยังบริการ บริการดังกล่าวสามารถถูกฉีดเข้าไปในตัวควบคุมของคุณ
อีกวิธีที่ดีที่นำมาจากบทความนี้ :
// base controller containing common functions for add/edit controllers
module.controller('Diary.BaseAddEditController', function ($scope, SomeService) {
$scope.diaryEntry = {};
$scope.saveDiaryEntry = function () {
SomeService.SaveDiaryEntry($scope.diaryEntry);
};
// add any other shared functionality here.
}])
module.controller('Diary.AddDiaryController', function ($scope, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
}])
module.controller('Diary.EditDiaryController', function ($scope, $routeParams, DiaryService, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
DiaryService.GetDiaryEntry($routeParams.id).success(function (data) {
$scope.diaryEntry = data;
});
}]);
คุณสามารถสร้างบริการและสืบทอดการทำงานของมันในคอนโทรลเลอร์ใด ๆ เพียงแค่ทำการฉีด
app.service("reusableCode", function() {
var reusableCode = {};
reusableCode.commonMethod = function() {
alert('Hello, World!');
};
return reusableCode;
});
จากนั้นในคอนโทรลเลอร์ของคุณที่คุณต้องการขยายจากบริการ reusableCode ด้านบน:
app.controller('MainCtrl', function($scope, reusableCode) {
angular.extend($scope, reusableCode);
// now you can access all the properties of reusableCode in this $scope
$scope.commonMethod()
});
DEMO PLUNKER: http://plnkr.co/edit/EQtj6I0X08xprE8D0n5b?p=preview
คุณสามารถลองสิ่งนี้ (ยังไม่ได้ทดสอบ):
function baseController(callback){
return function($scope){
$scope.baseMethod = function(){
console.log('base method');
}
callback.apply(this, arguments);
}
}
app.controller('childController', baseController(function(){
}));
คุณสามารถขยายด้วยการบริการ , โรงงานหรือผู้ให้บริการ มันเหมือนกัน แต่มีระดับความยืดหยุ่นต่างกัน
นี่คือตัวอย่างการใช้โรงงาน: http://jsfiddle.net/aaaflyvw/6KVtj/2/
angular.module('myApp',[])
.factory('myFactory', function() {
var myFactory = {
save: function () {
// saving ...
},
store: function () {
// storing ...
}
};
return myFactory;
})
.controller('myController', function($scope, myFactory) {
$scope.myFactory = myFactory;
myFactory.save(); // here you can use the save function
});
และที่นี่คุณสามารถใช้ฟังก์ชั่นการจัดเก็บได้เช่นกัน:
<div ng-controller="myController">
<input ng-blur="myFactory.store()" />
</div>
คุณสามารถใช้ $ controller โดยตรง (ตัวอย่าง 'ParentController', {$ scope: $ scope})
module.controller('Parent', ['$scope', function ($scope) {
//code
}])
module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) {
//extend parent controller
$controller('CtrlImpl', {$scope: $scope});
}]);
คุณสามารถใช้ Angular "เป็น" ไวยากรณ์รวมกับการสืบทอด JavaScript ธรรมดา
ดูรายละเอียดเพิ่มเติมได้ที่นี่ http://blogs.microsoft.co.il/oric/2015/01/01/base-controller-angularjs/
ฉันเขียนฟังก์ชันเพื่อทำสิ่งนี้:
function extendController(baseController, extension) {
return [
'$scope', '$injector',
function($scope, $injector) {
$injector.invoke(baseController, this, { $scope: $scope });
$injector.invoke(extension, this, { $scope: $scope });
}
]
}
คุณสามารถใช้สิ่งนี้:
function() {
var BaseController = [
'$scope', '$http', // etc.
function($scope, $http, // etc.
$scope.myFunction = function() {
//
}
// etc.
}
];
app.controller('myController',
extendController(BaseController,
['$scope', '$filter', // etc.
function($scope, $filter /* etc. */)
$scope.myOtherFunction = function() {
//
}
// etc.
}]
)
);
}();
ข้อดี:
จุดด้อย:
ฉันถือว่าการขยายตัวควบคุมเป็นการทำงานที่ไม่ดี ค่อนข้างทำให้ตรรกะที่ใช้ร่วมกันของคุณเป็นบริการ วัตถุที่ขยายในจาวาสคริปต์มีแนวโน้มที่จะค่อนข้างซับซ้อน หากคุณต้องการใช้การสืบทอดฉันจะแนะนำ typescript ถึงกระนั้นตัวควบคุมแบบบางก็เป็นวิธีที่ดีกว่าที่จะไปในมุมมองของฉัน