Last Updated: September 09, 2019
·
39.89K
· pyriku

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?

5 Responses
Add your response

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

over 1 year ago ·

@iotaweb Hi Rob, will post it on GitHub when I get some spare time. What problem are you getting anyway?

over 1 year ago ·

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!

over 1 year ago ·

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?

over 1 year ago ·

Thanks!! Great tutorial! Any idea how to add loading animation?

over 1 year ago ·