簡體   English   中英

$ timeout是避免AngularJS指令中jQuery插件渲染問題的唯一/推薦方法嗎?

[英]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 幾乎發生了什么事:

  1. Angular編譯這兩個元素;
  2. bx-slider元素的角度鏈接;
  3. bx-slider查找<li>元素(目前沒有)並創建列表;
  4. Angular鏈接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步中 ,滑塊組件復制最后一個元素,以便在第一個元素之前顯示它,以創建“連續性”印象(看小提琴以了解我的意思)。 所以現在您的流程就像:

  1. Angular編譯這兩個元素;
  2. bx-slider元素的角度鏈接;
  3. Angular鏈接ng-repeat並建立<li>列表;
  4. 您的代碼將調用父update函數,該函數將調用您的組件構建過程,該過程將復制最后一個元素。
  5. Angular解析綁定。

所以現在,您的問題是滑塊所進行的復制僅包含元素的模板,因為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的幻燈片解決方案

因此,總結一下:

  1. 您可以在解決方案中使用1毫秒的延遲,因為Angular摘要周期是同步的; - 但是您失去了將新項目添加到列表中的功能
  2. 您也可以將$scope.$last技巧與$timeout一起使用- 但您丟失了Angular綁定,並且如果此組件具有任何實例(已選擇,懸停),則可能會遇到問題
  3. 您可以使用已經寫好的解決方案(例如我建議的解決方案)。
  4. 您可以編寫自己的AngularJs本機解決方案。

我的回答似乎是回旋,但可能會消除您對$ 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM