Директива Angular.js с использованием stellar.js используется несколько раз - работает только первая

Я написал директиву angular.js, которая использует stellar.js для эффектов параллакса. Когда я вызываю одну и ту же директиву несколько раз одна за другой, stellar.js будет работать только с первой. Я читаю о $.stellar('refresh'); который должен повторно инициализировать звездный после введения элементов в DOM, но это не повлияло на меня. Также вызов звездного на окне не имел никакого эффекта.

Вот код моей директивы:

angular.module('ClientApp')
    .directive('parallaxModule', function () {
        return {
            templateUrl: 'views/parallax-module.html',
            restrict: 'E',
            scope: {
                data: '='
            },
            controller: function() {
               $(function(){
                   $(this).stellar({
                        horizontalScrolling: false,
                        verticalScrolling: true,
                        verticalOffset: 50
                   });
                });
            }
        };
    });

Вот как я использую директиву:

<parallax-module data="sectionData.para"></parallax-module>
<parallax-module data="sectionData.para"></parallax-module>

И вот мой шаблон:

<div class="parallax-module" data-stellar-background-ratio="0.2">
    <div class="wrapper" data-stellar-ratio="2.5">
        <div class="title">{{data.title}}</div>
        <div class="caption">{{data.caption}}</div>
    </div>
</div>

Вот плункер: http://plnkr.co/edit/TJbPL3dhSsiitZQWm9Qe?p=preview

3 ответа

Решение

$timeout работает, потому что предполагается, что звездный плагин применяется не к каждому элементу параллакса, а к контейнеру прокрутки. В примере в документах он применяется к window: $(window).stellar();

И так, с $timeout, код "ждет" конца очереди дайджеста, и когда он выполняется, все директивы загружаются. К сожалению, он выполняется для каждой директивы без необходимости.

Решение состоит в том, чтобы применить директиву к контейнеру. Тогда это становится так просто, как:

.directive("parallaxContainer", function(){
  return {
    link: function(scope, element){
      element.stellar({
          horizontalScrolling: false,
          verticalScrolling: true,
          verticalOffset: 0
        });
    }
  }
})

Это решение основано на наличии совместимого с звездой элемента (с data-stellar-background-ratioнапример) в этом контейнере:

<body parallax-container>
  <div data-stellar-background-ratio="0.2">
    ...
  </div>

  <div data-stellar-background-ratio="0.2">
    ...
  </div>
</body>

plunker

РЕДАКТИРОВАТЬ:

Приведенный выше код не будет работать, если элементы параллакса загружаются динамически. Это немного усложняет, но не сильно. В этом случае нам понадобится директива контейнера, отвечающая за запуск плагина, и фактические директивы элементов параллакса, которые будут регистрироваться в контейнере и уведомлять контейнер, когда они загрузятся.

Директива элемента может использовать ng-include получить уведомление о загрузке контента. Эта директива использует require: "^parallaxContainer",

app.directive('parallaxModule', function() {
    return {
      template: '<div ng-include="\'parallax-module.html\'" onload="notifyLoaded()"></div>',
      restrict: 'E',
      scope: {
        data: '='
      },
      require: "^parallaxContainer",
      link: {
        pre: function(scope, element, attrs, parallaxContainer){
          parallaxContainer.registerElement();
          scope.notifyLoaded = parallaxContainer.notifyElementLoaded;
        }
      }
    };
  })

parallaxContainer Директивы добавляет контроллер, который предоставляет методы для регистрации и уведомления. И он запускает плагин, когда все дети загружены

app.directive("parallaxContainer", function() {
    var parallaxElementCount = 0;
    var parallaxElementLoaded = 0;
    return {
      controller: function($element){

        this.registerElement = function(){
          parallaxElementCount++;
        };

        this.notifyElementLoaded = function(){
          parallaxElementLoaded++;

          if (parallaxElementCount !== 0 && parallaxElementCount === parallaxElementLoaded){

            $element.stellar({
              horizontalScrolling: false,
              verticalScrolling: true,
              verticalOffset: 0
            });
          }
        }
      },
    }
  })

plunker

Обратите внимание, что stellar не работает (по какой-то причине - возможно, есть некоторая информация об этом в звездных документах), когда он активируется более одного раза, поэтому после запуска загрузка большего количества дочерних элементов ничего не изменит.

Я нашел решение: поскольку звездный не является частью угловой структуры, я должен использовать $apply() для выполнения звездных выражений. Но так как $ digest уже выполняется в моей директиве, я должен использовать $timeout, чтобы получить вызов stellar() в другом цикле дайджеста. $ timeout неявно вызывает $ apply. Я также должен вызвать stellar() для angular.element($window), чтобы правильно запустить несколько директив.
Итак, вот мой рабочий код:

angular.module('ClientApp')
    .directive('parallaxModule', function ($timeout, $window) {
        return {
            templateUrl: 'views/parallax-module.html',
            restrict: 'E',
            scope: {
                data: '='
            },
            link: function() {
                $timeout(function () {
                    angular.element($window).stellar({
                        horizontalScrolling: false,
                        verticalScrolling: true,
                        verticalOffset: 50
                    });
                });
            }
        };
    });

Использовать директиву NG-PARALLAX для Angular2/Angular4 очень просто.

Ссылка: https://www.npmjs.com/package/ng2-parallaxscroll

Git: https://github.com/tyrantwave/ng2-parallaxscroll

Пример 1:

<div parallax [config]="{axis: x, speed: -.3}" style=" background-image: url('/parallax_img.jpg');"></div>

Пример 2:

<ng-parallax img="img/img.jpg" [config]="{axis: x, speed: -.3}" class="some-class"></ng-parallax>
Другие вопросы по тегам