Ответ 1
Quick -n-Dirty it (демонстрация)
Мы хотели бы убедиться, что все изображения загружены, поэтому напишите директиву для этого:
app.directive('loadDispatcher', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('load', function() {
scope.$emit('$imageLoaded');
});
}
};
})
& hellip; и присоедините его к элементам ng-src
'd:
<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!
}
});
Разделить его (демо)
Это немного беспокоит, так как он не придерживается Закона Деметры — любая директива, которая будет смотреть событие $imageLoaded
, должна знать о модели (scope.videos.objects.length
).
Мы можем предотвратить эту связь, выяснив, сколько изображений было загружено без явной адресации модели. Предположим, что события будут обрабатываться в ng-repeat
.
-
Удостоверьтесь, что
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;
}
});
}
};
});
& hellip; который будет применен к элементу ng-repeat
ed:
<div ng-repeat="object in videos.objects" class="slide" image-load-watcher>
Теперь мы можем просто смотреть $allImagesLoaded
, например. в слайдере:
scope.$on('$allImagesLoaded', function() {
_initSlider(element);
});
Обобщите его (если хотите) (демо)
Мы можем снова сломать его и применить этот подход в приложении для использования диспетчеризации событий для любой загрузки ng-repeat
или ng-src
, что не всегда необходимо (1) но может быть весьма полезным. Посмотрим, как:
-
Украсьте директиву
ng-src
, поэтому она отправляет событие при загрузке изображения: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; }); // ... });
-
Украсьте
ng-repeat
, чтобы сообщить, когда он закончится: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. Украшение должно быть тщательно рассмотрено, так как в результате будет отправлено событие на каждое изображение, загруженное через приложение.
& bull; Переопределение функции link
будет невозможно в версии 1.3.x и далее.