在遍历数据后,我遇到一些有关DOM操作的问题。
我们有一个与数据绑定并且可以正常工作的jQuery滑块插件,但是当使用时ng- repeat,我们必须将其初始化包装起来$timeout以使其起作用—现在甚至不起作用。
ng- repeat
$timeout
我认为使用$timeout是不可靠的,这将导致错误的修复。在jQuery中,我可以使用$(document).ready()-这很可靠,但是使用angular.element(document).ready()似乎也不起作用。
$(document).ready()
angular.element(document).ready()
调用了Slider指令,但由于没有将图像加载到DOM中而无法获得滑块中图像的高度-导致滑块的计算高度为0。
我现在感到非常沮丧-必须有一种方法来ng-repeat循环处理数据(例如)之后的DOM 。
ng-repeat
滑块的初始化如下:
var sliderLoad = function () { $timeout(function () { var setHeight = elem.find('.slide:eq(0)').outerHeight(true); elem.css({ height: setHeight }); }, 1000); // Show the slider nav buttons elem.parent().find('.direction-nav').show(); };
……这是一个 复制演示 。
我们要确保所有图像都已加载,因此让我们为此编写一个指令:
app.directive('loadDispatcher', function() { return { restrict: 'A', link: function(scope, element, attrs) { element.bind('load', function() { scope.$emit('$imageLoaded'); }); } }; })
…并将其附加到ng-src‘d元素:
ng-src
<img class="thumb-recipe" ng-src="{{ object.tile_url }}" load-dispatcher/>
现在,我们可以比较模型捕获的事件数量,并根据需要进行操作:
var loadCount = 0; scope.$on('$imageLoaded', function () { if (loadCount++ === scope.videos.objects.length - 1) { _initSlider(); // act! } });
这有点令人不安,因为它不遵守Demeter律 -任何监视$imageLoaded事件的指令都必须知道模型(scope.videos.objects.length)。
$imageLoaded
scope.videos.objects.length
我们可以通过查找加载多少图像而无需显式寻址模型来防止这种耦合。假设事件将在中处理ng-repeat。
$last
.controller('LoopWatchCtrl', function($scope) { $scope.$watch('$last', function(newVal, oldVal) { newVal && $scope.$emit('$repeatFinished', $scope.$index); }); }) <div ng-repeat="object in videos.objects" ng-controller="LoopWatchCtrl">
var loadCount = 0, lastIndex = 0; scope.$on('$repeatFinished', function(event, data) { lastIndex = data; }); scope.$on('$imageLoaded', function() { if (lastIndex && loadCount++ === lastIndex) { _initSlider(element); // this is defined where-ever } });
在那里,现在我们的指令不必了解模型。但是,这有点麻烦,现在我们必须将指令 和 控制器绑定在一起。
让我们将整个shabang提取到一个指令中:
app.directive('imageLoadWatcher', function($rootScope) { return { restrict: 'A', link: function(scope, element, attrs) { if (typeof $rootScope.loadCounter === 'undefined') { $rootScope.loadCounter = 0; } element.find('img').bind('load', function() { scope.$emit('$imageLoaded', $rootScope.loadCounter++); }); }, controller: function($scope) { $scope.$parent.$on('$imageLoaded', function(event, data) { if ($scope.$last && $scope.$index === $rootScope.loadCounter - 1) { $scope.$emit('$allImagesLoaded'); delete $rootScope.loadCounter; } }); } }; });
…将应用于ng-repeated元素:
<div ng-repeat="object in videos.objects" class="slide" image-load-watcher>
现在我们可以简单地$allImagesLoaded在滑块中观看例如:
$allImagesLoaded
scope.$on('$allImagesLoaded', function() { _initSlider(element); });
我们可以再次对其进行分解,并在整个应用程序中应用此方法,以对 任何 ng-repeat完成或ng-src负载使用事件分发,这并非总是必要的( 1 ),但可能非常有用。让我们看看如何:
app.config(function($provide) { $provide.decorator('ngSrcDirective', function($delegate) { var directive = $delegate[0], link = directive.link; directive.compile = function() { return function(scope, element, attrs) { link.apply(this, arguments); element.bind('load', function() { scope.$emit('$imageLoaded'); }); }; }; return $delegate; }); // ... });
app.config(function($provide) { // ... $provide.decorator('ngRepeatDirective', function($delegate) { var directive = $delegate[0], link = directive.link; directive.compile = function() { return function(scope, element, attrs) { link.apply(this, arguments); scope.$watch('$$childTail.$last', function(newVal, oldVal) { newVal && scope.$emit('$repeatFinished'); }); }; }; return $delegate; }); });
var repeatFinished = false; var loadCount = 0; scope.$on('$repeatFinished', function() { repeatFinished = true; }); scope.$on('$imageLoaded', function () { if (repeatFinished && loadCount++ === scope.videos.objects.length - 1) { _initSlider(); // this is defined where-ever } });
当我们回到第一个问题时,这似乎无法达到目的,但它可能非常强大。还有-看,妈妈,没有新指令!
<div ng-repeat="object in videos.objects" class="slide"> <img class="thumb-recipe" ng-src="{{ object.tile_url }}"/> </div>
TL;DR, just gimme tha demo!!!
1 。必须仔细考虑修饰,因为结果将在整个应用程序中加载的每个图像上发送一个事件。
• 从link1.3.x版开始,将无法覆盖该功能。
link