angular-viewportmise

Angular directive that executes a promise when it is in the viewport. It also adds some classes to DOM to know if it is loading, loaded on in an error status. # How it works? angular-viewportmise exports a directive called ``vpm``. This directive may be used like this: ```html <!-- loader: Must be function returning a promise. --> <!-- loadOnViewport: True to load on viewport otherwise false. --> <div data-vlm data-loader="::getTestData" data-load-on-viewport="true"> </div> ``` angular-viewportmise will execute the method in two cases: - If loadOnViewport === true: When it is inside the viewport - If loadOnViewport === false: On load. Moreover, angular-viewportmise will add 3 classes depending on the status of the directive: - **vpm-loading**: When getData is set - **vpm-loaded**: When getData success - **vpm-error**: When getData fails # Third party libs used - angular-inview

HTML

<script src="https://rawgit.com/thenikso/angular-inview/master/angular-inview.js"></script>
<body data-ng-app="app">
  <div data-ng-controller="mainCtrl">
    <div data-ng-repeat="i in ::iterator">
      <div data-ng-repeat="j in [0,1,2,3,4,0,1,2,3,4]">
      <div data-ng-controller="testCtrl">
        <!-- This directives will be executed inside viewport-->
        <p data-vpm data-loader="::getTestData"
            data-load-on-viewport="true"
            data-ng-bind="::data">
        </p>
        </div>
      </div>
    </div>
    <div data-ng-controller="testCtrl">
      <!-- This directive will be executed without being on viewport-->
      <p data-vpm data-loader="::getTestData"
          data-ng-bind="::data">
      </p>
    </div>
  </div>
</body>

CSS

/* Styles go here */

.vpm {
  border: 5px solid grey;
  margin: 5px;
  height: 50px;
}

.vpm-loading {
  background-color: yellow;
}

.vpm-error {
  background-color: red;
}

.vpm-loaded {
  background-color: green;
}

JavaScript

angular.module('app', [
  'angular-viewportmise'
]);

angular.module('app')
  .controller('mainCtrl', mainCtrl)
  .controller('testCtrl', testCtrl);

/********************************************
 * MAIN AND TEST CONTROLLERS (FOR TESTING)
 *******************************************/
function mainCtrl($scope, $q, $timeout) {
  var i = 50;

  $scope.iterator = [];
  while (i--) {
    $scope.iterator.push(i);
  }

  $scope.getChannels = function() {
    var deferred = $q.defer();

    $timeout(function() {
      $scope.channel = 'Test content';

      deferred.resolve();
    }, 500);

    return deferred.promise;
  };
}

function testCtrl($scope, $timeout, $q) {
  $scope.getTestData = function() {
    var deferred = $q.defer();

    $timeout(function() {
      $scope.data = 'Test data ' + new Date();

      deferred.resolve();
    }, 500);

    return deferred.promise;
  };
}

/********************************************
 * ViewPortMise MODULE
 *******************************************/
angular.module('angular-viewportmise', [
  'angular-inview'
]);

angular.module('angular-viewportmise')
  .directive('vpm', vpmDrct);

function vpmDrct() {
  return {
    template: '<div data-ng-transclude class="vpm" ' +
      'data-in-view="load($inview)"></div>',
    restrict: 'A',
    scope: {
      loadOnViewport: '=',
      loader: '='
    },
    transclude: true,
    replace: true,
    link: function(scope, element) {
      var clearGetDataWatcher;
      
      /**
       * Executes data loader and set classes depending on the status into
       * the CSS.
       */
      function _execLoader() {
        if (scope.loader) {
          scope.loader()
            .then(function() {
              element.removeClass('vpm-error');
              element.addClass('vpm-loaded');
            })
            .catch(function() {
              element.removeClass('vpm-loaded');
              element.addClass('vpm-error');
            })
            .finally(function() {
             ...