[英]Is $timeout the only/recommended way to avoid jQuery plugin rendering problems in AngularJS directives?
我正在將jQuery Webapp移植到AngularJS(<-初學者!)。
為了集成bxSlider和一些模板化的東西,我編寫了以下指令:
[編輯]最好看看jsFiddle jsfiddle.net/Q5AcH/2/ [/ Edit] 。
angular.module('myApp')
.directive('docListWrapper', ['$timeout', function ($timeout) {
return {
restrict: 'C',
templateUrl: 'partials/doc-list-wrapper.html',
scope: { docs: '=docs'},
link: function (scope, element, attrs) {
$timeout(function () {
element
.children('.doc-list')
.not('.ng-hide')
.bxSlider(); // <-- jQuery plugin doing heavy DOM manipulation
}, 100); // <-------------- timeout in millis
}
};
}]);
如果沒有$timeout
則存在一個問題,即bxSlider無法計算新創建的元素的大小或根本找不到它們。
我有點擔心,使用較長的超時值可能會導致閃爍,而使用較短的值可能會在慢速計算機上引起問題。
在我的實際應用程序中(當然比jsFiddle中包含更多的數據和更多的部分),我觀察到了一些奇怪的東西:
當我嘗試使用超時值時,使用10毫秒或更多毫秒就足夠了,因此jQuery插件bxSlider可以找到完整的DOM。 等待時間更少(9毫秒或更短),該插件無法包裝<ul>
。
但是,仍然存在非常討厭的閃爍的問題。
可能是由於DOM較小,在閃爍中,只有Internet Explorer 10才在Chrome + Firefox中看不到閃爍。
我不想依賴$timeout
經驗值,它可能高度依賴於機器,操作系統,渲染引擎,角度版本,血液壓力,...
有健壯的解決方法嗎?
我發現了一些事件監聽器示例( $on
, $emit
)以及ng-repeat $scope.$last
完成的一些魔術。 如果我可以消除閃爍,那么我會接受組件之間的某種耦合,即使這與AngularJS的雄心也不相稱。
您的問題是賽車狀況問題,因此您不能僅刪除$timeout
。 幾乎發生了什么事:
bx-slider
元素的角度鏈接; bx-slider
查找<li>
元素(目前沒有)並創建列表; ng-repeat
並構建<li>
列表並解析綁定。 因此,要解決競爭條件的第一個方面(僅在所有<li>
准備就緒后才構建組件),應在bxSlider
指令中公開一個update
方法,並創建一個子指令,該子指令將在bxSlider
控制器中調用update
函數,使用$scope.$last
技巧 :
.directive('bxSlider', function () {
var BX_SLIDER_OPTIONS = {
minSlides: 2,
maxSlides: 7,
slideWidth: 120
};
return {
restrict: 'A',
require: 'bxSlider',
priority: 0,
controller: function() {},
link: function (scope, element, attrs, ctrl) {
var slider;
ctrl.update = function() {
slider && slider.destroySlider();
slider = element.bxSlider(BX_SLIDER_OPTIONS);
};
}
}
}])
.directive('bxSliderItem', function($timeout) {
return {
require: '^bxSlider',
link: function(scope, elm, attr, bxSliderCtrl) {
if (scope.$last) {
bxSliderCtrl.update();
}
}
}
})
該解決方案甚至使您能夠向模型添加新的iten,對於每當您有新的$last
項時,都會構建bxSlider。 但是同樣,您會遇到另一種賽車狀況。 在第3步中 ,滑塊組件復制最后一個元素,以便在第一個元素之前顯示它,以創建“連續性”印象(看小提琴以了解我的意思)。 所以現在您的流程就像:
bx-slider
元素的角度鏈接; ng-repeat
並建立<li>
列表; update
函數,該函數將調用您的組件構建過程,該過程將復制最后一個元素。 所以現在,您的問題是滑塊所進行的復制僅包含元素的模板,因為Angular尚未解決其綁定問題。 因此,每當循環列表時,您都會看到損壞的內容。 要解決它,只需添加 1毫秒的$timeout
就足夠了,因為您將交換第4步和第5步的順序,因為Angular綁定解析與$digest
循環位於同一堆棧中,所以您應該沒有問題:
.directive('bxSliderItem', function($timeout) {
return {
require: '^bxSlider',
link: function(scope, elm, attr, bxSliderCtrl) {
if (scope.$last) {
$timeout(bxSliderCtrl.update, 1);
}
}
}
})
但是您有一個新問題,因為Slider復制邊界元素,AngularJ的摘要循環不會概述這些重復,因此您失去了在這些組件內部進行模型綁定的功能。
在完成所有這些之后,我建議您使用僅已適應angularjs的幻燈片解決方案 。
因此,總結一下:
$scope.$last
技巧與$timeout
一起使用- 但您丟失了Angular綁定,並且如果此組件具有任何實例(已選擇,懸停),則可能會遇到問題 我的回答似乎是回旋,但可能會消除您對$ timeout的需求。 嘗試制作另一個指令並將其附加到li
元素。 類似於以下偽代碼:
angular.module('myApp').directive('pdfClick', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$element.bxSlider().delegate('a', 'click', pdfClicked);
}
}
});
<li class="doc-thumbnail" ng-repeat="doc in docs" pdfClick>
它應該將click事件附加到ng repeat生成的每個列表項的錨點上。
數據在渲染時尚未到達范圍!
原來的問題是在執行(鏈接)指令時數據不存在。
在小提琴中,范圍內的數據訪問非常快。 在我的應用程序中,由於它是通過$http
加載的,因此花費了更多時間。 這就是為什么在大多數情況下$timeout
<10ms不足的原因。
所以我的解決方案是
angular.module('myApp')
.directive('docListWrapper', ['$timeout', function ($timeout) {
return {
restrict: 'C',
templateUrl: 'partials/doc-list-wrapper.html',
scope: { docs: '=docs'},
link: function (scope, element, attrs) {
$timeout(function () { // <-------------------- $timeout
element
.children('.doc-list')
.not('.ng-hide')
.bxSlider();
}, 10);
}
};
}]);
我現在有這個:
angular.module('myApp')
.directive('docListWrapper', [function () {
return {
restrict: 'C',
templateUrl: 'partials/doc-list-wrapper.html',
scope: { docs: '=docs'},
link: function (scope, element, attrs) {
scope.$watch('docs', function () { // <---------- $watch
element
.children('.doc-list')
.not('.ng-hide')
.bxSlider();
});
}
};
}]);
也許對於這個問題有一個更優雅的解決方案,但是現在我很高興它能起作用。
我希望這對其他AngularJS初學者有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.