加载Ng-Repeat元素后运行的jQuery Swiper脚本

时间:2015-05-21 10:28:00

标签: jquery angularjs angularjs-directive angularjs-ng-repeat swiper

我正在使用AngularJS,jQuery,HTML,CSS和Bootstrap创建一个Web应用程序,我想从我的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插件,您可以在this link看到一些演示。

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:

Working with 3 separated divs

不使用ng-repeat:

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 个答案:

答案 0 :(得分:11)

在开始如何使这项工作成功之前,大多数这些类型的jQuery插件都不能很好地处理角度,因为它们会对角度不知道的DOM进行修改。

话虽这么说,诀窍是推迟调用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后更改模型,也可能会看到意外情况。但是,如果您的模型是静态的,这应该适合您。如果您的模型发生变化,那么您可能希望搜索更多&#34; angular&#34;轮播指令。

答案 1 :(得分:3)

有一种更简单的方法可以将其用于Swiper.js。初始化swiper时,只需将observer属性设置为true即可。这将使Swiper监视幻灯片的更改,因此在通过ng-repeat添加所有幻灯片之前,您不必担心它是否会运行。

示例:

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

来自观察者属性的Swiper文档:

  

设置为true以在Swiper及其元素上启用Mutation Observer。在这种情况下,如果您更改其样式(如隐藏/显示)或修改其子元素(如添加/删除幻灯片),Swiper将每次更新(重新初始化)

答案 2 :(得分:0)

我正在回答这个老问题,因为有些人要我这样做,因为我想帮助其他开发人员处理这个“有趣”的问题。

经过一年多的时间,我设法使用PromisesFactories解决了这个问题。

基本上,我做了FactoryItemFactory确保获得所需的所有项目(在本例中为幻灯片),然后,我又创建了另一个FactorySwiperFactory,它使用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();可以删除,但这不是必需的
  • 一般来说,这种方法适用于异步任务

上面的代码完成了工作,但请注意我在下面说的内容

  

尝试避免在AngularJS项目中使用jQuery库,例如iDangero.us Swiper。相反,寻找一个Angular库来完成同样的工作。如果回来的话我就像现在这样熟练,我从来没有用过它,尤其是$(document).ready的东西。相反,研究Promises或AngularJS这样做的方式。我对AngularJS和Web Development一般都是新手,所以我做出了错误的决定。

我希望我一直很有帮助。