Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute function in controller after scroll event AngularJS

Let's say I have a feed of articles that stack:

<div ng-repeat="article in articles">
  <h1> {{article.title}} </h1>
  <p> {{article.body}} </p>
</div>

When scrolling past each article:

<div scroll="atNewArticle(article)" ng-repeat="article in articles">
  ...
</div>

I want to execute a function:

.controller('AppCtrl', ['$scope', function($scope) {
  $scope.atNewArticle = function(){
    console.log(article);
  }
}])

I'm having difficulty figuring out the right way to do this because I'm not sure if I should bind the scroll event to the window or detect the offset of the directive element itself.

Here's what I've tried so far: http://jsfiddle.net/6VFJs/1/

like image 971
Dan Kanze Avatar asked Dec 26 '13 20:12

Dan Kanze


2 Answers

There are a few things that are probably a good idea (depending on your exact use case). Firstly, a few non-Angular-specific ones:

  • Use a "debounced" version of the listener to the scroll event, so that the function only executes once the user has stopped scrolling. You can use lodash/underscore library for this.

    var debounced = _.debounce(function() { .... }, 500); // Executes 500ms after last call of the debounced function.
    angular.element($document).on('scroll', debounced);
    
  • Use a library to determine position/visibility of any element. verge.js is a small lightweight library I've found for this. One way of using this is:

    var visible = verge.inViewport(element);
    

    But depending on what exactly you want to treat as "visible", you might need to change this.

  • Keep track of what is visible / not visible at every event (as you have a function "atNewArticle", which suggests you only want it called when an item comes into view). So you'll need to create an array of visible elements, and test it on scroll.

    var index = visibleElements.indexOf(scope.scrollItem);
    

The Angular-specific point:

  • Pass 2 options to the directive. 1) the callback function when a new item comes into view, and 2) the article item itself, so it can be passed back to the callback.

    scope: {
         scroll: '&',
         scrollItem: '='
    }
    

    These can be used as:

    <div ng-repeat="article in articles" class="block" scroll="atNewArticle(item)" scroll-item="article">
    

    This gives the directive's scope a variable scrollItem, and a function scroll. So the directive can call, at the appropriate point:

    scope.scroll({item:scope.scrollItem});
    

    You should note the syntax of this: it's an key-object map.

You can see all this in action at http://jsfiddle.net/Pb8t4/4/

Edit: updated the link to a jsfiddle that includes a call to scope.$apply so that any changes are properly noticed by Angular.

like image 108
Michal Charemza Avatar answered Oct 22 '22 05:10

Michal Charemza


You can find this example helpful.

The idea is to use: scroll binding with if statement that filters scope.$apply(attrs.scroll); calling

like image 6
Maxim Shoustin Avatar answered Oct 22 '22 04:10

Maxim Shoustin