尝试使用两个非常简单的自定义指令来使用3d party jQuery库:
无法让ng-click工作,也不确定如何从链接函数中的重复元素中获取数据。
当您点击幻灯片时,它的名称和隐藏数据应该附加在它下面的列表中。
angular.module('sm', [])
.directive('selector', function () {
return {
restrict: "E",
template: '<div class="swiper-wrapper">' +
'<div class="swiper-slide" ng-repeat="slide in slides">' +
'<h1 ng-click="selected(slide)">{{ slide.name }}</h1>' +
'</div></div>',
replace: true,
controller: ['$scope', '$timeout', function ($scope, $timeout) {
$scope.slides = [{
name: 'one',
hidden: 'kittens'
}, {
name: 'two',
hidden: 'puppies'
}, {
name: 'three',
hidden: 'bacon'
}];
$timeout(function () { // important!
$.swiper.init();
});
// ng-click never fired due to the jQuery slider plugin
$scope.selected = function (data) {
console.log('ng-click called $scope.selected');
$scope.$broadcast('slideSelected', data);
};
}],
link: function linkFn(scope, lElement, attrs) {
lElement.on('click', function (el) {
console.log('lElement on click called');
// how do I get access to the clicked element's data?
scope.$broadcast('slideSelected', el);
$
})
}
}
})
.directive('selected', function () {
return {
restrict: "E",
template: '<ul>' +
'<li ng-repeat="selection in selected">{{ selection }}</li>' +
'</ul>',
replace: true,
controller: ['$scope', function ($scope) {
var selected = ['Add to me', 'please'];
$scope.selected = selected;
$scope.$on('slideSelected', function (data) {
$scope.$apply(function () {
selected.push(selected);
})
});
}],
}
})
.controller('MyCtrl', function ($scope) {});
$.swiper = {
init: function () {
var mySwiper = $('.swiper-container').swiper({
mode: 'horizontal',
loop: true
});
}
};
答案 0 :(得分:4)
这里有一些注意事项:
1。如果您没有以这样的方式创建指令,即子指令应该能够require
并且可以访问其控制器,那么您可以考虑使用链接函数而不是控制器。 $timeout
依赖关系可以移动到指令工厂函数。
2。你的指令正在分享范围;由于没有告诉指令创建一个新的或隔离范围,它们各自的scope.selected
属性(一个函数和另一个函数)相互覆盖。
隔离范围可以解决此问题,但是由于不再连接范围,因此您无法执行scope.$broadcast
。你的选择是
scope.$parent.$broadcast
$rootScope
(所有范围的最终父母) 3。如果你看一下the documentation for Scope#$on
,你会发现监听器函数的第一个参数是被触发的事件; 第二个参数将是您发送到$broadcast
函数的自定义数据。
4。在1.1.x版本的Angular中,如果没有添加ng-repeat
子句来告诉Angular应该使用哪些数据来确定数据是否真的重复,则不能在track by
属性中包含相同的数据。我们在这里使用$index
:
<li ng-repeat="selection in selected track by $index">{{ selection }}</li>
解决这些问题会让我们看到这段代码:http://jsfiddle.net/BinaryMuse/hCdJA/;问题是jQuery插件仍在使用ng-click
。在Angular中使用第三方jQuery插件时,这类问题并不少见,答案通常是编写一个指令来包装插件的功能。
经过一番努力,我有一套包含Swiper功能的指令(至少我们关心的一点; Swiper在API方面有相当宽的表面积,所以我没有涵盖所有)一种相当可重用的方式。我很难让setData
和getData
正常工作(我怀疑这是插件中的一个错误)所以最后通过常规的data()
来电和外部的黑客攻击用于存储回调的对象。
在我们进入代码之前,您可以在此处看到一个有效的演示:http://jsfiddle.net/BinaryMuse/UruNG/
这是最终的HTML:
<div ng-app="sm">
<div ng-controller="MyCtrl">
<swiper>
<slide ng-repeat="slide in slides" ng-click="select(slide)">
<h1>{{slide.name}}</h1>
</slide>
</swiper>
<ul>
<li ng-repeat="item in items track by $index">{{item | json}}</li>
</ul>
</div>
</div>
我已将swiper
和slide
元素拆分,以使其可重用且可组合; slide
指令使用require
属性来获取父swiper
指令定义的控制器,以访问它公开的函数。
这是让它工作的JavaScript:
angular.module('sm', [])
.directive('swiper', function($timeout) {
return {
restrict: 'EA',
template: "<div class='swiper-container'>" +
"<div class='swiper-wrapper'></div>" +
"<div style='display: none' ng-transclude></div>" +
"</div>",
replace: true,
transclude: true,
// We use a controller here so the slide directive
// can require it and call `addSlide`.
controller: function($element) {
var newSlides = [];
var mySwiper = null;
var slideCount = 0;
var callbacks = {};
// Attached directly to the controller so other directives
// have access to it.
this.addSlide = function(html, callback) {
if (mySwiper) {
var newSlide = mySwiper.createSlide(html.html());
// Hackily save off the callback based on
// a unique ID since getData() for
// swiper.clickedSlide doesn't appear to work
// when using setData() on newSlide.
newSlide.data('slideNumber', ++slideCount);
mySwiper.appendSlide(newSlide);
callbacks[slideCount] = callback;
mySwiper.swipeTo(0, 0, false);
} else {
// mySwiper hasn't been initialized yet; save
// the slide off in an array so we can add it later.
newSlides.push({html: html, callback: callback});
}
};
$timeout(function() {
mySwiper = $element.swiper({
mode: 'horizontal',
loop: true,
onSlideClick: function(swiper) {
// Look up the callback we saved off and call it.
var clicked = swiper.clickedSlide;
var slideNumber = clicked.data('slideNumber');
var callback = callbacks[slideNumber];
if (callback) callback();
}
});
// Now that mySwiper has been initialized, iterate
// over any calls to `addSlide` that happened
// before we were ready and add them to the swiper.
for (var i = 0; i < newSlides.length; i++) {
var slide = newSlides[i];
this.addSlide(slide.html, slide.callback);
}
}.bind(this));
}
}
})
.directive('slide', function() {
return {
restrict: 'EA',
// Look for a parent `swiper` element and get its controller
require: '^swiper',
template: "<div class='swiper-slide' ng-transclude></div>",
replace: true,
transclude: true,
link: function(scope, elem, attrs, swiper) {
swiper.addSlide(elem, function() {
scope.$apply(attrs.ngClick);
});
}
}
})
.controller('MyCtrl', function ($scope) {
$scope.slides = [{
name: 'one',
hidden: 'kittens'
}, {
name: 'two',
hidden: 'puppies'
}, {
name: 'three',
hidden: 'bacon'
}];
$scope.items = ["Add to me", "please"];
$scope.select = function(slide) {
$scope.items.push(slide);
};
});
您可以看到我们已经设法将所有特定于Swiper的功能保留在指令中,而我们循环的数据(slides
)和要回拨的数据(select
)附加到控制器范围,它们更有意义(因为它们是特定于应用程序的数据)。
同样,可以在此处找到工作演示:http://jsfiddle.net/BinaryMuse/UruNG/
答案 1 :(得分:1)
我注意到了几件事:
1)根本不需要链接功能。当您使用模板时,angular负责编译模板内的指令。此外,您的链接函数绑定到选择器元素,而不是单独绑定每个li,因此您无法确定单击哪个数据对象的原因。
2)您的指令正在使用继承的范围,但都将不同的东西分配给同一属性名称。在selector指令中,您将$scope.selected
指定为函数。在selected指令中,您将$scope.selected
指定为值数组。这些干扰是因为它们使用相同的范围。我能够通过将第一个更改为$scope.select = function(data)...
来修复它。
3)您编写了事件处理程序以查找数据作为第一个参数。该事件是第一个参数,之后的任何参数都绑定到广播事件时传递的参数,所以它更像是$scope.$on('slideSelected', function(event, data)...
。
4)您的事件处理程序不需要应用范围,因为这将自动发生,而只是更新模型。
更新的小提琴是here。