API calls and infinite scroll with AngularJS
This weekend I've been playing around a little bit with AngularJS, and I have to say that I'm gladly surprised about it. For illustrate how simple it can be, I'm going to show a minimal example for loading images from an API when the user scrolls down.
First, let's take a look to the HTML:
<!DOCTYPE html>
<html>
<head>
<title>Some dummy example</title>
<script type="text/javascript" src="/static/js/vendor/angular-1.0.6.min.js"></script>
<script type="text/javascript" src="/static/js/files_controllers.js"></script>
</head>
<body ng-controller="FilesListCtrl" ng-app="my-awesome-app">
<div class="container" when-scrolled="loadImages()">
<h2>Images</h2>
<ul class="images">
<li ng-repeat="image in images">
<img ng-src="{{ image.thumbnailLink }}">
{{ image.title }}
</li>
</ul>
</div>
</body>
</html>
Pretty simple isn't? Some comments:
We added ng-app
to the body
tag. That way we "allow" AngularJS to work over it and do its magic. Also, we added a ng-controller
controller called FilesListCtrl
that we will wrote. In AngularJS, a controller is a function that takes a $scope
. That function can do almost everything you want to, and you can add anything you want to it. The cool thing is that everything you add to the scope, will be accessible from the templates.
Then, we have when-scrolled="loadImages()"
. We linked that event to a function that we will write in our JS code.
Lastly, we have this chunk of code:
<li ng-repeat="image in images">
<img ng-src="{{ image.thumbnailLink }}">
{{ image.title }}
</li>
The directive ng-repeat
is basically a for in loop, so we're iterating over a variable called images
and for every single one, we're adding a <li>
with an image and its title. Notice that we're using ng-src
instead of src
. Basically is for don't getting a DOM error all the time.
Finally, let's take a look to the JS file:
function FilesListCtrl($scope, $http) {
$scope.images = [];
$scope.next_page = null;
var in_progress = true;
$scope.loadImages = function() {
if (in_progress){
var url = '/api/v1/files.json';
if ($scope.next_page) {
url = $scope.next_page;
}
$http.get(url).success(function(data) {
$scope.images = $scope.images.concat(data.items);
$scope.next_page = data.nextPageInternal;
if (!$scope.next_page) {
in_progress = false;
}
});
}
};
FilesListCtrl.$inject = ['$scope', '$http'];
$scope.loadImages();
}
This first chunk is the controller itself. It's executed when the page is loaded and basically what we're doing is making an API call and adding the images to $scope.images
. That way we will be able to access them through the HTML we saw before. The line FilesListCtrl.$inject = ['$scope', '$http'];
is needed to overcome some problems with minimization.
Also we have there:
angular.module('my-awesome-app', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
This way we bind the directive whenScrolled
and trigger the function we marked on the DOM. With the proper logic inside loadImages()
we will get an effect really "pinterest-like" when scrolling.
Easy enough isn't?
Written by Pablo Recio
Related protips
5 Responses
Hi Pablo,
Nice tutorial thanks. I am trying to reproduce this locally, but without success - do you have a working example to share?
Regards, Rob
@iotaweb Hi Rob, will post it on GitHub when I get some spare time. What problem are you getting anyway?
i guess that in the directive you binding to the scroll event of that div. but usually, this one doesn't scroll, unless you have given it a height and it's children do overflow.
i've solved this for me now like this, listening to scroll-event of the window:
link: function(scope, element, attrs) {
var $myWindow = angular.element($window);
$myWindow.bind('scroll', function() {
var elementHeight = element.height();
var scrollAmount = $myWindow.scrollTop();
var delta = 10;
if (elementHeight - (scrollAmount + delta) < 0) {
scope.$apply(attrs.whenScrolled);
}
});
}
still nice article, thank you!
I got it working nicely here, but I'm seeing one problem when running this in a scrollable DIV. Every time I scroll to the bottom it loads extra data and jumps back up to the top of the list. How can I prevent this?
Thanks!! Great tutorial! Any idea how to add loading animation?