скрипт jQuery Swiper для запуска после загрузки элементов ng-Repeat

я делаю веб-приложение, используя AngularJS, jQuery, HTML, CSS и Bootstrap, и я хотел бы выбрать некоторые ссылки на изображения из моего JSON, который находится на сервере Apache2, и использовать их для отображения этих изображений на моей главной странице. Я также хотел бы провести их как на карусели. Чтобы это сработало, я пытаюсь использовать иданжеро.us Swiper.

когда я выбираю свои изображения с 3 отдельными дивами, у меня нет проблем. Я получаю свои изображения, а затем я могу нормально салфетки их как я хочу. Я делаю это, как показано ниже:

Main.HTML-код:

<div ng-init="initGetRequestMain()">

  <div class="swiper-slide" ng-click="swipers()" 
              style="{{data.background1}}"></div>
  <div class="swiper-slide" ng-click="swipers()" 
              style="{{data.background2}}"></div>
  <div class="swiper-slide" ng-click="swipers()" 
              style="{{data.background3}}"></div>

   <script src="scripts/custom/main/swipers.js"></script>
</div>

я использую Swiper для прокрутки с одного изображения на другое, и, похоже, он работает так, как должен. Это плагин jQuery, и вы можете увидеть некоторые демонстрации в этой ссылке.

Swipers.js:

angular.module('swipers', [])
       .controller('',[ 
        $(document).ready(function (){
           var swiper = new Swiper('.swiper-container',{
           direction: 'horizontal',
           pagination: '.swiper-pagination',
           paginationClickable: true
       })
})]);

Json:

"background1":"background-image: url(images/img1.jpg)",
"background2":"background-image: url(images/img2.jpg)",
"background3":"background-image: url(images/img3.jpg)"

mainController.js:

  myApp.controller('MainController', ["$scope","$http",                
                  function($scope,$http){

      $scope.initGetRequestMain = function(){

       $http.get('http://localhost/main.json').success(function(data){

         $scope.data=data;
       })
      }
  }]);

проблема что когда я пытаюсь использовать ng-repeat вместо 3 отдельных divs я больше не вижу их, и мой скрипт Swiper запускается до их полной загрузки. У меня нет ошибок в моей консоли или в моем JSON (проверено с помощью JSONLint). Ниже я добавил 2 скриншота моего вывода в обеих ситуациях.

работа с 3 отдельными дивами:

Working with 3 separated divs

не работает с НГ-повторяю:

Not working with ng-repeat

это код, где я пытаюсь сделать ng-repeat работа с тем же контроллером и тем же скриптом Swiper, что и раньше:

Main.HTML-код:

  <div ng-init="initGetRequestMain()">

       <div ng-repeat="slide in data.slides" isLoaded="">
           <div class="swiper-slide" style="{{slide.background}}" 
                       ng-click="swipers()"></div>
       </div>

    <script type="text/javascript-lazy" src="scripts/custom/main/swipers.js"></script>
  </div>

mainJson.в JSON:

"slides":[
            {"background":"background-image:url('images/img1.jpg')"},
            {"background":"background-image:url('images/img2.jpg')"},
            {"background":"background-image: url('images/img3.jpg')"}
],

чтобы загрузить мои изображения перед запуском скрипта, я пытаюсь использовать 2 пользовательских директивы.

isLoaded говорит мне, когда последний ng-repeat элемент загружен и устанавливает pageIsLoaded = true;:

myApp.directive('isLoaded', function (){

   return{
     scope:true,
     restrict: 'A', //Attribute type
     link: function (scope, elements, arguments){ 

        if (scope.$last === true) {
            scope.pageIsReady = true;
            console.log('page Is Ready!');
         }
     }   
   }
})

gettingTheScript ждет pageIsLoaded = true; и загружает скрипт:

myApp.directive('src', function (){

     return{
       scope:true,
       restrict: 'A', //Attribute type
       link: function (scope, elements, arguments){

            scope.$on(pageIsReady===true, function(){
                   if (attr.type === 'text/javascript-lazy'){

                       scope.scriptLink = arguments.src;
                    }
             })
         },
         replace: true, //replaces our element 
         template: '{{scriptLink}}'
       }
  })   

они, похоже, не решают мою проблему. Я тоже не вижу console.log('page Is Ready!'); при создании первого.

у меня просто есть некоторые проблемы, когда я должен вызвать скрипт, как Swiper после загрузки страницы, чтобы избежать таких проблем. У моих образов нет высоты. Я думаю, что проблема вызвана ng-repeat не полностью загрузка перед запуском моего скрипта.

что я делаю не так? Есть ли лучшие решения?

3 ответов


прежде чем перейти к тому, как сделать эту работу, большинство из этих типов плагинов jQuery не работают хорошо с angular, поскольку они делают изменения в DOM, о которых angular не знает.

это, как говорится, трюк откладывает вызов плагина jQuery до тех пор, пока Angular не отобразит DOM.

во-первых, поместите плагин swiper в директиву:

.directive('swiper', function($timeout) {
    return {
        link: function(scope, element, attr) {
            //Option 1 - on ng-repeat change notification
            scope.$on('content-changed', function() {
                new Swiper(element , {
                    direction: 'horizontal',
                    pagination: '.swiper-pagination',
                    paginationClickable: true);
            }
            //Option 2 - using $timeout
            $timeout(function(){
                new Swiper(element , {
                    direction: 'horizontal',
                    pagination: '.swiper-pagination',
                    paginationClickable: true);
            });

        }
    };
})

для варианта 1 вам нужна измененная isLoaded директива

myApp.directive('isLoaded', function (){

   return{
     scope:false, //don't need a new scope
     restrict: 'A', //Attribute type
     link: function (scope, elements, arguments){ 

        if (scope.$last) {
            scope.$emit('content-changed');
            console.log('page Is Ready!');
        }
     }   
   }
})

наконец, ваша разметка (обратите внимание на изменение с isLoaded на is-loaded....это, вероятно, причина, по которой вы не видели уведомление).

<div ng-init="initGetRequestMain()" swiper>

   <div ng-repeat="slide in data.slides" is-loaded>
       <div class="swiper-slide" style="{{slide.background}}" 
                   ng-click="swipers()"></div>
   </div>
</div>

как уже упоминалось, большинство плагинов jQuery, которые выполняют функциональность карусели, не обрабатывают изменения в DOM (i.e новые элементы) очень хорошо. Даже с обоими вариантами вы можете увидеть неожиданные вещи, если вы измените свою модель после первого отображения ng-repeat. Однако, если ваша модель статична, это должно работать для тебя. Если ваша модель изменится, вам может потребоваться поиск более "угловой" директивы карусели.


есть еще более простой способ, которым вы можете использовать, который запечен в Swiper.js. Когда вы инициализируете swiper, просто установите свойство observer в true. Это заставит Swiper следить за изменениями ваших слайдов, поэтому вам не придется беспокоиться о том, работает ли он до того, как все слайды будут добавлены через ng-repeat.

пример:

var galleryTop = new Swiper('.gallery-top', { observer: true });

и из документации Swiper по свойству observer:

установите значение true для включения мутации Наблюдатель на Swiper и его элементов. В этом случае Swiper будет обновляться (повторно инициализироваться) каждый раз, если вы измените его стиль (например, Скрыть/показать) или измените его дочерние элементы (например, добавление/удаление слайдов)


я отвечаю на этот вопрос, потому что некоторые ребята просили меня сделать это и потому что я хочу помочь другим разработчикам этой "смешной" проблемы.

после более чем одного года мне удалось решить эту проблему с помощью Promises и Factories.

по сути, я сделал Factory, ItemFactory сделать, чтобы получить все необходимые элементы (в данном случае слайды), затем я сделал еще один Factory, SwiperFactory, который использует Promise чтобы вызвать этого смешного парня,swipers(). Я назвал все эти заводские методы в MainController, который после получения слайдов и запуска скрипта делает $scope.apply(); чтобы отразить изменения в главном представлении.

Main.HTML-код:

//MainController known as mainCtrl
<div ng-repeat="slide in mainCtrl.slides">
  <div class="swiper-slide" ng-click="mainCtrl.swipers()"></div>
</div>

MainController.js:

angular
.module('myApp')
.controller('MainController', MainController);

function MainController($scope, ItemFactory, SwiperFactory) {

   var vm    = this;
   //Initializing slides as an empty array
   vm.slides = [];

   /** Gets called as soon as the Controller is called **/
   getData('main.json');

   function getData(path){
      ItemFactory.getItems(path).then( function(response){

        //If response is not empty
        if (response) {
           //Assigning fetched items to vm.items
           vm.slides = response.data;
        }

        //Calling triggerSwiper after we ensured fetching item data
        SwiperFactory.triggerSwiper();

        //Callig $scope.$apply() to reflect correctly the changes in the view
        $scope.$apply();
      })
   };

}

ItemFactory.js:

angular
.module('myApp')
.factory('ItemFactory', function ($http) {

    /** Using this referring as ItemFactory Controller **/
    var vm     = this;
    vm.API_URL = 'http://localhost/';

    /** Gets items from API based on jsonPath **/
    function getItems(jsonPath) {
        return $http.get(vm.API_URL + jsonPath
        ).then(function (response) {
            return (response);
        });
    }

    //Exposing getItems method
    return {
        getItems : getItems
    }
});

SwiperFactory.js:

angular
.module('myApp')
.factory('SwiperFactory', function () {

    /** Using this referring as SwiperFactory Controller **/
    var vm     = this;

    /** Triggers Swiper Script 
     *  Resolving only after swiper has been triggered **/
    function triggerSwiper(){
      return new Promise( function(resolve, reject){
        resolve({
            $(document).ready(function (){
                var swiper = new Swiper('.swiper-container',{
                direction           : 'horizontal',
                pagination          : '.swiper-pagination',
                paginationClickable : true
            })
        });

      }, function(error){
         console.log('Error while triggering Swiper');
      })

    }

    //Exposing triggerSwiper method
    return {
        triggerSwiper : triggerSwiper
    }
});
  • $scope.$apply(); может быть удален, это не действительно важно
  • в общем, такой подход хорошо работает с асинхронными задачами

код выше делает работу, но, пожалуйста, обратите внимание на то, что я говорю ниже

попробуйте избежать использования библиотек jQuery, таких как iDangero.американский Swiper в проекте AngularJS. Вместо этого, найдите угловую библиотеку, которая делает ту же работу. Если бы тогда я был опытным, как сейчас, я бы никогда не использовал его, особенно для $(document).ready материал. Вместо этого изучите обещания или AngularJS способ сделать это. Я был совершенно новичком в AngularJS и веб-разработке в целом, поэтому я принял неправильные решения.

надеюсь, я был полезен.