加载Ng-Repeat元素后运行的jQuery Swiper脚本 [英] jQuery Swiper script to run after Ng-Repeat elements are loaded

查看:141
本文介绍了加载Ng-Repeat元素后运行的jQuery Swiper脚本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用AngularJS,jQuery,HTML,CSS和Bootstrap制作一个Web应用程序,我想从位于Apache2服务器中的JSON中选择一些图像链接,并使用它们来渲染这些图像.我的主页.我也想像旋转木马一样擦拭它们.为此,我尝试使用 iDangero.us滑动器.

当我选择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在触发我的脚本之前未完全加载引起的.

我做错了什么?有更好的解决方案吗?

解决方案

在开始进行此工作之前,大多数这些类型的jQuery插件都无法与angular一起使用,因为它们对angular所做的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之后更改模型,您也可能会看到意想不到的事情.但是,如果您的模型是静态的,那么这应该对您有用.如果您的模型发生了变化,那么您可能希望搜索更有角度的"轮播指令.

I'm making a web app using AngularJS, jQuery, HTML, CSS and Bootstrap, and I would like to pick some image links from my JSON that is located in an Apache2 server and use them in order to render those images in my main page. I would also like to swipe them like in a carousel. To make that work, I'm trying to use iDangero.us Swiper.

When I pick my images with 3 separated divs, I have no problems. I get my images and then I can normally swipe them as I want. I do it like shown below:

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>

I use Swiper to swipe from one image to another, and it seems to work as it should. It's a jQuery plugin, and you can see some demos at 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;
       })
      }
  }]);

The problem is that when I try to use ng-repeat instead of 3 separated divs, I can't see them anymore and my Swiper script triggers before they are fully loaded. I have no errors in my console or in my JSON (validated with JSONLint). Below, I added 2 screenshots of my output in both situations.

Working with 3 separated divs:

Not working with ng-repeat:

This is the code where I try to make ng-repeat work keeping the same controller and the same Swiper script as before:

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')"}
],

In order to get my images loaded before triggering the script, I'm trying to use 2 custom directives.

isLoaded tells me when the last ng-repeat element is loaded and sets 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 waits for pageIsLoaded = true; and loads the script:

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}}'
       }
  })   

They do not seem to fix my problem. I also can't see console.log('page Is Ready!'); when making the first one.

I'm just having some troubles when I have to trigger a script like Swiper after the page is loaded in order to avoid these kind of problems. My images seem to have no height. I think that the problem is caused by ng-repeat not fully loading before triggering my script.

What am I doing wrong? Are there better solutions?

解决方案

Before getting into how to make this work, most of these types of jQuery plugins don't work well with angular since they make modifications to the DOM that angular doesn't know about.

That being said, the trick is deferring the invocation of the jQuery plugin until after Angular has rendered the DOM.

First, put your swiper plugin into a directive:

.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);
            });

        }
    };
})

For Option 1, you need a modified isLoaded directive

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!');
        }
     }   
   }
})

Finally, your markup (note the change from isLoaded to is-loaded....this is probably the reason you weren't seeing the notification).

<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>

As mentioned, most jQuery plugins that do carousel functionality don't handle changes to the DOM (i.e new elements) very well. Even with both options, you may see unexpected things if you change your model after the ng-repeat is first rendered. However, if your model is static, this should work for you. If your model changes, then you might want to search for a more "angular" carousel directive.

这篇关于加载Ng-Repeat元素后运行的jQuery Swiper脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆