jQuery 加载 Ng-Repeat 元素后将脚本滑动到 运行
jQuery Swiper script to run after Ng-Repeat elements are loaded
我正在使用 AngularJS、jQuery、HTML、CSS 和 Bootstrap 制作网络应用程序,我想选择一些来自位于 Apache2 服务器中的 JSON 的图像链接,并使用它们在我的主页中呈现这些图像。我也想像在旋转木马中一样滑动它们。为了做到这一点,我正在尝试使用 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:
不适用于 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 一起使用,因为它们对 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 后更改模型,您也可能会看到意想不到的事情。但是,如果您的模型是静态的,这应该适合您。如果您的模型发生变化,那么您可能需要搜索更多 "angular" 轮播指令。
我回答这个老问题是因为有些人要求我这样做,因为我想帮助其他开发人员解决这个 "funny" 问题。
一年多后,我使用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();
可以去掉,不是必须的
- 一般来说,这种方法适用于异步任务
上面的代码就可以了,但是,请注意我下面说的
Try avoiding using jQuery libraries such as iDangero.us Swiper in an AngularJS project. Instead, look for an Angular library which makes the same work. If back then I was skilled like I'm right now, I would have never used it, expecially for the $(document).ready
stuff. Instead, study Promises or AngularJS way of doing that. I was totally new to AngularJS and Web Development in general, so I took wrong decisions.
希望对您有所帮助。
Swiper.js 中包含了一种更简单的使用方法。初始化滑动器时,只需将观察者 属性 设置为 true。这将使 Swiper 监视您的幻灯片的更改,因此您不必担心它是否在通过 ng-repeat 添加所有幻灯片之前运行。
示例:
var galleryTop = new Swiper('.gallery-top', {
observer: true
});
来自观察者的 Swiper 文档 属性:
Set to true to enable Mutation Observer on Swiper and its elements. In this case Swiper will be updated (reinitialized) each time if you change its style (like hide/show) or modify its child elements (like adding/removing slides)
我正在使用 AngularJS、jQuery、HTML、CSS 和 Bootstrap 制作网络应用程序,我想选择一些来自位于 Apache2 服务器中的 JSON 的图像链接,并使用它们在我的主页中呈现这些图像。我也想像在旋转木马中一样滑动它们。为了做到这一点,我正在尝试使用 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:
不适用于 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 一起使用,因为它们对 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 后更改模型,您也可能会看到意想不到的事情。但是,如果您的模型是静态的,这应该适合您。如果您的模型发生变化,那么您可能需要搜索更多 "angular" 轮播指令。
我回答这个老问题是因为有些人要求我这样做,因为我想帮助其他开发人员解决这个 "funny" 问题。
一年多后,我使用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();
可以去掉,不是必须的- 一般来说,这种方法适用于异步任务
上面的代码就可以了,但是,请注意我下面说的
Try avoiding using jQuery libraries such as iDangero.us Swiper in an AngularJS project. Instead, look for an Angular library which makes the same work. If back then I was skilled like I'm right now, I would have never used it, expecially for the
$(document).ready
stuff. Instead, study Promises or AngularJS way of doing that. I was totally new to AngularJS and Web Development in general, so I took wrong decisions.
希望对您有所帮助。
Swiper.js 中包含了一种更简单的使用方法。初始化滑动器时,只需将观察者 属性 设置为 true。这将使 Swiper 监视您的幻灯片的更改,因此您不必担心它是否在通过 ng-repeat 添加所有幻灯片之前运行。
示例:
var galleryTop = new Swiper('.gallery-top', {
observer: true
});
来自观察者的 Swiper 文档 属性:
Set to true to enable Mutation Observer on Swiper and its elements. In this case Swiper will be updated (reinitialized) each time if you change its style (like hide/show) or modify its child elements (like adding/removing slides)