Скрипт jQuery Swiper, който да се изпълнява след зареждане на Ng-Repeat елементи

Създавам уеб приложение, използвайки AngularJS, jQuery, HTML, CSS и Bootstrap, и бих искал да избера някои връзки към изображения от моя JSON, който се намира в Apache2 сървър, и да ги използвам, за да изобразя тези изображения в главната си страница . Бих искал също да ги плъзна като във въртележка. За да направя това да работи, се опитвам да използвам iDangero.us Swiper.

Когато избирам изображенията си с 3 разделени div, нямам проблеми. Получавам изображенията си и след това мога нормално да ги плъзгам, както искам. Правя го както е показано по-долу:

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 разделени div, вече не мога да ги видя и моят скрипт Swiper се задейства, преди да са напълно заредени. Нямам грешки в моята конзола или в моя JSON (проверен с JSONLint). По-долу добавих 2 екранни снимки на моя изход и в двете ситуации.

Работа с 3 отделни div:

„Работа

Не работи с 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 не се зарежда напълно, преди да задейства моя скрипт.

какво правя грешно Има ли по-добри решения?


person AndreaM16    schedule 21.05.2015    source източник
comment
Трябва да използвате директива за това   -  person Keval Bhatt    schedule 21.05.2015
comment
@KevalBhatt Можете ли да бъдете по-конкретен? Благодаря.   -  person AndreaM16    schedule 21.05.2015
comment
Тъй като вие тъжите, вашият скрипт се задейства, преди да се зареди, така че трябва да напишете една директива за това и в директивата да използвате $timeout. Когато работите от страна на angular, тогава за манипулиране на DOM винаги използвайте директива, защото първо се изпълнява javascript, след това другият ви код, така че как трябва angular да знае, че вашият javascript код ще извърши някои манипулации върху вашите изображения   -  person Keval Bhatt    schedule 21.05.2015
comment
jsfiddle.net/dLrj3200 вижте, че имам същия проблем в owl-itemSlider, така че използвам една директива, вижте този пример и внедрите на вашия и ме уведомете, ако искате помощ   -  person Keval Bhatt    schedule 21.05.2015
comment
@KevalBhatt И така, трябва ли да направя директива, където да извикам своя скрипт swipers.js?   -  person AndreaM16    schedule 21.05.2015
comment
можеш ли да създадеш цигулка за това, за да мога да създам директива за това   -  person Keval Bhatt    schedule 21.05.2015
comment
@KevalBhatt Не работи, разбира се, защото не съм поставил всичко jsfiddle.net/Lrttkuhz Но бихте ми спасили деня, ако можете да напишете подходяща директива за моята ситуация, само за пример, дори и да не работи на fiddle.   -  person AndreaM16    schedule 21.05.2015
comment
Нека продължим тази дискусия в чата.   -  person Keval Bhatt    schedule 21.05.2015
comment
Вероятно трябва да разгледате предварителното зареждане на изображенията, преди действително да подготвите DOM за функцията за прелитане...   -  person deostroll    schedule 03.06.2015
comment
Както каза @KevalBhatt, една директива почти ще реши проблема вместо вас. Директива, която слуша функцията onload на всяко изображение. Така че просто трябва да проверите дали броят на заредените изображения е равен на общия брой изображения, които искате да заредите, и след това да задействате плъзгача. Имах нещо подобно в един от моите проекти. Ако все още имате проблем, уведомете ме и мога да го потърся вместо вас   -  person Sylvan D Ash    schedule 05.06.2015
comment
Защо сте маркирали w3schools?   -  person Omar Tariq    schedule 18.03.2017


Отговори (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 (т.е. нови елементи). Дори и при двете опции може да видите неочаквани неща, ако промените модела си след първото изобразяване на ng-repeat. Въпреки това, ако вашият модел е статичен, това трябва да работи за вас. Ако вашият модел се промени, тогава може да искате да потърсите по-„ъглова“ директива за въртележка.

person Joe Enzminger    schedule 31.05.2015
comment
Вашето решение изглежда доста добро. Отивам за решението 1°. Опитвам нещо подобно на това (в случай че пулкът няма да работи, просто копирам и поставям някакъв код там, за да го видите), сега мога да виждам изображенията си с пълна ширина (дадох на бащата swiper-wrapper 100% 100%), но скриптът изглежда не работи (но е импортиран правилно). Както и да е, импортирам всичките си скриптове, но swipers в моя index.html, импортирам swipers.js в моя main, където е необходимо. Някаква идея? - person AndreaM16; 31.05.2015
comment
Преместете импортирането на swiper.js във вашата страница index.html. - person Joe Enzminger; 31.05.2015
comment
Ако отделите време, за да съставите нещо, което възпроизвежда проблема, ще видя дали мога да разбера къде грешите. - person Joe Enzminger; 01.06.2015
comment
Изглежда, че го реших, работейки върху вашето решение. Благодаря, +50 за вас! - person AndreaM16; 05.06.2015
comment
Радвам се, че сте го оправили! - person Joe Enzminger; 05.06.2015

Има още по-лесен начин, който можете да използвате, който е вграден в Swiper.js. Когато инициализирате swiper, просто задайте свойството наблюдател на true. Това ще накара Swiper да следи за промени във вашите слайдове, така че няма да се налага да се притеснявате дали работи, преди всички слайдове да бъдат добавени чрез ng-repeat.

Пример:

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

И от документацията на Swiper за свойството наблюдател:

Задайте true, за да активирате Mutation Observer на Swiper и неговите елементи. В този случай Swiper ще се актуализира (инициализира повторно) всеки път, ако промените неговия стил (като скриване/показване) или промените дъщерните му елементи (като добавяне/премахване на слайдове)

person Luke    schedule 22.08.2016
comment
Не проработи за мен... Това беше грешката: Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': параметър 1 не е от тип 'Node'. - Успях да го отстраня, изглежда, че в 3804 целевата променлива е недефинирана, наблюдателят може да добавя наблюдателите към дъщерни възли, но внезапно спира след намиране на недефинирана - което нямам представа защо се случва. - person Eduardo Almeida; 06.01.2018

Отговарям на този стар въпрос, защото някои момчета ме помолиха да го направя и защото искам да помогна на други разработчици с този "забавен" проблем.

След повече от една година успях да разреша този проблем с помощта на 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.us Swiper в AngularJS проект. Вместо това потърсете Angular библиотека, която прави същата работа. Ако тогава бях умел като сега, никога нямаше да го използвам, особено за нещата $(document).ready. Вместо това проучете Promises или AngularJS как да направите това. Бях напълно нов в AngularJS и уеб разработката като цяло, така че взех грешни решения.

Надявам се да съм била полезна.

person AndreaM16    schedule 26.07.2016