$ timeout是避免AngularJS指令中jQuery插件呈现问题的唯一/推荐方法吗?

hgo*_*ebl 1 javascript jquery angularjs angularjs-directive

我正在将一个jQuery webapp移植到AngularJS(< - beginner!).

为了整合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
            }
        };
    }]);
Run Code Online (Sandbox Code Playgroud)

没有$timeout问题,bxSlider无法计算新创建的元素的大小或根本找不到它们.

我有点担心使用长超时值可能会导致闪烁而使用短值可能会导致慢速机器出现问题.

在我的实际应用程序中(当然有更多数据和更多部分而不是jsFiddle)我发现了一些奇怪的东西:

当我使用超时值时,使用10或更多毫秒就足够了,所以jQuery插件bxSlider找到一个完整的DOM.等待时间较少(9毫秒或更少),插件无法正常包装<ul>.

但是仍然存在非常讨厌的闪烁问题.

在小提琴中,可能是由于DOM较小,Chrome + Firefox中只有Internet Explorer 10才会出现闪烁现象.

我不想依赖于经验值,因为$timeout它可能高度依赖于机器,操作系统,渲染引擎,角度版本,血液预测......

有一个强大的解决方法吗?

我已经找到了一些带有事件监听器($on,$emit)的例子,以及使用ng-repeat完成的一些魔术$scope.$last.如果我可以消除闪烁,我会接受组件之间的一些耦合,即使这不符合AngularJS的野心.

Cai*_*nha 5

你的问题是赛车状况问题,所以你不能只是删除$timeout.几乎所发生的是:

  1. Angular编译两个元素;
  2. Angular链接你的bx-slider元素;
  3. bx-slider查找<li>元素(此时没有)并创建列表;
  4. Angular链接ng-repeat并构建<li>列表并解析绑定.

因此,要解决竞速条件的第一个方面(仅在所有<li>准备就绪后构建组件),您应该updatebxSlider指令中公开一个方法并创建一个子指令,该子指令将使用技巧调用控制器中的update函数:bxSlider$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();
            }
        }
    }
})
Run Code Online (Sandbox Code Playgroud)

此解决方案甚至可以让您将新$last的itens 添加到模型中,因为每次有新项目时,都会构建bxSlider.但同样,你会遇到另一种竞争条件.在第3步中,滑块组件复制最后一个元素,以便在第一个元素之前显示它,以创建"连续性"印象(看看小提琴以理解我的意思).所以现在你的流程如下:

  1. Angular编译两个元素;
  2. Angular链接你的bx-slider元素;
  3. Angular链接ng-repeat并构建<li>列表;
  4. 您的代码调用父update函数,该函数调用组件构建过程,该函数复制最后一个元素;
  5. Angular解析绑定.

所以现在,你的问题是滑块所做的重复只带有元素的模板,因为Angular还没有解析它的绑定.因此,无论何时循环列表,您都会看到内容破碎.要解决它,只需添加$timeout 1毫秒就足够了,因为你要交换步骤4和5的顺序,因为Angular绑定解析在与$digest循环相同的堆栈中发生,所以你应该没有问题:

.directive('bxSliderItem', function($timeout) {
    return {
        require: '^bxSlider',
        link: function(scope, elm, attr, bxSliderCtrl) {
            if (scope.$last) {
                $timeout(bxSliderCtrl.update, 1);
            }
        }
    }
})
Run Code Online (Sandbox Code Playgroud)

但是你有一个新问题,因为Slider复制了边界元素,AngularJs摘要周期没有对这些复制进行概述,因此你失去了这些组件内部模型绑定的能力.

毕竟,我建议你使用一个已经适应的angularjs幻灯片解决方案.

所以,总结一下:

  1. 你可以在解决方案中使用1毫秒的延迟,因为Angular摘要周期是同步的; - 但您失去了将新项目添加到列表中的功能
  2. 你可以使用一个$scope.$last技巧$timeout- 但你丢失了Angular绑定,如果这个组件有任何实例(选中,悬停),你会遇到问题
  3. 你可以使用已经编写的解决方案(就像我建议的那样).
  4. 你可以编写自己的AngularJs原生解决方案.