Reputation: 3502
I have an app that allows paginating through a large text dataset via the keyboard's right/left arrow keys. The data that is displayed, also contains images.
I would like to delay the loading of these images for 1-2 seconds while the user is paginating through the data quickly.
As soon as the user stops at a certain page, all images should be "lazily" loaded.
I tried to adapt this fiddle to my app without success (see below). http://jsfiddle.net/boneskull/UfrhH/3/
my controller code
$scope.delay_images = function() {
$timeout(function() {
return true;
}, 2000);
};
view code
<tbody>
<tr>
<td style='text-align:left;padding-right:40px; height:250px;width:{{100 / panel.size}}%' ng-repeat="event in data| slice:panel.offset:panel.offset+panel.size">
<h4>{{event._source.Price}} {{event._source.Currency}}</h4>
<img ng-show="delay_images" ng-animate="{show: 'show'}" style='height:100px' ng-src="/img/{{event._source.image_paths}}"/><br /><h6>{{event._source.Title}}</h6>
<button ng-click="build_search(event._source)" ng-show='event._source.ClusterID' type="button" class="btn btn-primary">More like this</button>
</td>
</tr>
</tbody>
I am fairly new to angular.js so any tips that can guide me into the right direction are greatly appreciated.
Thanks!
UPDATE
This is a partially working code based on the accepted answer. BTW, it also implements a delay for the keydownevent.
Right now the code uses ng-show to hide and show images. I realized that this approach is problematic because it triggers a lot of image resource requests in the background while the user is paging.
So the best would be to combine the current solution somehow with the second solution replacing the src= attribute.
How would I go about that? I am catching the keydownevent from a surrounding div, so I have no direct handle to the images. Is there a way to access a class of elements with angular js? Just like with a jquery selector?
var lastFire = 0;
$scope.changeIndex = function($event) {
if($event.keyCode == 37 || $event.keyCode == 39){
var cFire = new Date();
// cancel old timeout
if ($scope.panel.timeoutId) {
$timeout.cancel($scope.panel.timeoutId);
}
// $event.keyCode...
if ((cFire - lastFire) / 1000 > 0.3){
// hide images to start with
$scope.panel.imagesVisible = false;
if ($event.keyCode == 37 && $scope.panel.offset > 0){
$scope.panel.offset = $scope.panel.offset - 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 1000);
}
if ($event.keyCode == 39 && $scope.panel.offset < $scope.data.length - 10){
$scope.panel.offset = $scope.panel.offset + 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 1000);
}
}
else{
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 1000);
}
}
};
Upvotes: 3
Views: 3891
Reputation: 3502
Working solution...
used the element method from angular which wraps jquery. Also included the apply method in between the timeouts to make the ui experience smoother. Thanks for your input guys.
var lastFire = 0;
$scope.changeIndex = function($event) {
if($event.keyCode == 37 || $event.keyCode == 39){
var cFire = new Date();
// cancel old timeout
if ($scope.panel.timeoutId) {
$timeout.cancel($scope.panel.timeoutId);
}
// $event.keyCode...
if ((cFire - lastFire) / 1000 > 0.5){
if ($event.keyCode == 37 && $scope.panel.offset > 0){
$scope.panel.offset = $scope.panel.offset - 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.$apply(function () {
angular.forEach(angular.element('.productImage'), function(value, key){
var elem = angular.element(value);
elem.attr('src',elem.attr('ng-slow-src'));
});
$scope.panel.timeoutId = undefined;
});
}, 1000);
}
if ($event.keyCode == 39 && $scope.panel.offset < $scope.data.length - 10){
$scope.panel.offset = $scope.panel.offset + 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.$apply(function () {
angular.forEach(angular.element('.productImage'), function(value, key){
var elem = angular.element(value);
elem.attr('src',elem.attr('ng-slow-src'));
});
$scope.panel.timeoutId = undefined;
});
}, 1000);
}
}
else{
$scope.panel.timeoutId = $timeout(function() {
$scope.$apply(function () {
angular.forEach(angular.element('.productImage'), function(value, key){
var elem = angular.element(value);
elem.attr('src',elem.attr('ng-slow-src'));
});
$scope.panel.timeoutId = undefined;
});
}, 1000);
}
}
};
Upvotes: 0
Reputation: 29234
I think the simplest thing would be to handle it when you are paging. Let's say this is the function you call to page:
$scope.changeOffset = function(offset) {
$scope.panel.offset = offset;
// cancel old timeout
if ($scope.panel.timeoutId) {
$timeout.cancel($scope.panel.timeoutId);
}
// hide images to start with
$scope.panel.imagesVisible = false;
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 2000);
});
Then all images on the page can use the flag:
<img ng-show="panel.imagesVisible" ...
Upvotes: 2
Reputation: 7588
I'd make a custom directive. This is off the top of my head, untested, but something like this should work:
myApp.directive('ngSlowSrc', function($timeout){
return{
restrict: 'A',
link: function(scope, element, attrs){
$timeout(function() {
element.src = attrs.ngSlowSrc;
}, 2000);
}
}
});
now just make your image calls like this: <img ng-slow-src="myimage.jpg" >
The way this works is it just waits 2 seconds to set the src of your image.
Upvotes: 4