[英]Improve AngularJS directive code
我寫了一個AngularJS指令,但我對它很新,我不知道我是否采用了“Angular方式”......
以下是我的代碼: http ://plnkr.co/edit/X1tOk4z8f6dCK3mfB7HP?p=preview
HTML:
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<meta charset=utf-8 />
<title>Directive Test</title>
<script src="script.js"></script>
</head>
<body ng-controller="MainCtrl">
<button id="button1" ng-click="dummyClickFoo()" wait-button="foo"><i></i> Foo</button>
<button id="button2" ng-click="dummyClickBar()" wait-button="bar"><i></i> Bar</button>
</body>
</html>
JS:
app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.dummyClickFoo = function() {
$scope.startSpinner('foo');
setTimeout(function() {
$scope.stopSpinner('foo');
}, 3000);
};
$scope.dummyClickBar = function() {
$scope.startSpinner('bar');
setTimeout(function() {
$scope.stopSpinner('bar');
}, 3000);
};
});
app.directive('waitButton', function() {
return {
restrict: 'A',
controller: ['$scope', '$element', function($scope, $element) {
$scope.startSpinner = function(id) {
var el = angular.element(document.querySelector('[wait-button="'+id+'"]'));
el.children('i').text('searching...');
};
$scope.stopSpinner = function(id) {
var el = angular.element(document.querySelector('[wait-button="'+id+'"]'));
el.children('i').empty();
};
}]
};
});
我發現document.querySelector('[wait-button="'+id+'"]')
部分,它有點“討厭”......(或不是?); 否則我不知道在同一個控制器中不同時間重復使用同一指令的更好方法。 有人可以建議我更好的代碼嗎?
謝謝。
我主張使用link
函數來處理這類事情:
link: function($scope, elem, attrs){ /* do something w. elem */ }
在控制器中訪問元素並不是很有角度 。 這是指令對象的link
和compile
功能的全部要點....
......但在極少數情況下,這是合理的。 控制器中注入的$element
引用了與angular.element(document.querySelector('[wait-button="'+id+'"]'))
代碼相同的內容。 此時您只需要使用$element
。 但我可以推薦一種更有棱角的方法嗎?
另一個問題是你如何將你的意圖從指令傳達到主控制器並返回到指令。 您的用例與大多數用例略有不同,因為您具有異步性質。
我已經做了一個利用隔離范圍和回調參數的例子。 在大多數現實場景中,您將處理異步回調的承諾。 因此,我使用.finally
邏輯來執行回調,該回調與指令進行通信,無論異步邏輯是什么。
在我的例子中要記住的事情:
link
函數而不是自定義指令控制器時,有一個模糊的線。 $scope
范式。 plunker - http://plnkr.co/edit/0AvlCQW5qqkpYKl2WpB3?p=preview
主控制器
.controller 'MainCtrl', class MainCtrl
@$inject = [
'$scope'
'$interval'
]
constructor: ($scope, @$interval)->
@viewData = 'Skynet 2.0'
@isLoading = false
callbackExample: ($callbackFunc)->
@loadRqst()
.finally -> $callbackFunc?()
loadRqst: ->
@isLoading = 1
# this returns a promise which gets processed in the example functions
@$interval =>
console.log @isLoading++
, 250, 10
.finally =>
@isLoading = false
實施ui
<button callback-btn="vc.callbackExample($callbackFunc)">
Callback Example<i> - I'm loading & I'm #1</i>
</button>
<button callback-btn="vc.callbackExample($callbackFunc)">
Callback Example<i> - Look I can load too, I'm #2</i>
</button>
CSS
[callback-btn] i{
display: none;
}
[callback-btn].loading i{
display: initial;
}
.directive 'callbackBtn', ($parse)->
dir =
restrict: 'A'
scope: { callbackBtn: '&' }
link: ($scope, elem, attrs)->
onCallback = ->
console.log 'on callback'
elem.removeClass 'loading'
elem.on 'click', ->
elem.addClass 'loading'
$scope.$apply ->
$scope.callbackBtn({$callbackFunc: onCallback})
我強烈建議你轉換
<button id="button1" ng-click="dummyClickFoo()" wait-button="foo"><i></i> Foo</button>
到一個指令,所以你的代碼將是
<my-button label="foo"></my-button>
directive
的整個想法是促進關注點的分離。
當你使用指令時,它的所有功能都應該由指令本身來處理。
現在,你的MainCtrl
有一部分指令邏輯,理想情況下你的指令應該在那里。
此外,在您的指令中,還有一個控制器功能。 理想情況下,控制器功能應該只處理視圖模型之間傳遞的數據等。
對於DOM正在進行的操作,它應該在link
函數中實現。
因此,做同樣事情的angular way
如下。
你的Html
<body ng-controller="MainCtrl">
<button id="button1" wait-button="foo"><i></i> Foo</button>
<button id="button2" wait-button="bar"><i></i> Bar</button>
<!-- NOTICE: No ng-click handlers here -->
</body>
你的MainCtrl
app.controller('MainCtrl', function($scope) {
// No code required here. This will be handled in Directive's link function.
});
你的waitButton
指令。
app.directive('waitButton', function() {
return {
restrict: 'A',
controller: ['$scope', '$element', function($scope, $element) {
//Again, it's not controller's job to handle DOM manipulations. So code required here.
}],
link: function($scope, $element){
var waitButtons = angular.element(document.querySelectorAll('[wait-button]'));// NOTE: I've used querySelectorAll instead of querySelector. This will make this function generic.
waitButtons.on('click', function(){
var $this = angular.element(this);
startSpinner($this);
setTimeout(function(){
stopSpinner($this);
},3000);
});
function startSpinner(el) {
el.children('i').text('searching...');
}
function stopSpinner(el) {
el.children('i').empty();
}
}
};
});
將您的函數移動到指令中。 將單擊處理程序放在鏈接函數中。
app.directive('waitButton', function($timeout) {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
var startSpinner = function() {
elem.children('i').text('searching...');
};
var stopSpinner = function() {
elem.children('i').empty();
};
var clickHandler = function() {
startSpinner();
$timeout(stopSpinner,3000);
};
elem.on("click", clickHandler);
}
}
});
還可以使用AngularJS $timeout
服務。 有關它的更多信息,請參閱AngularJS $超時服務API參考 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.