javascript - AngularJS - Directives, ng-repeat, ng-click and Jquery -
trying 3d party jquery library working 2 pretty simple custom directives:
can't ng-click work , not sure how data repeated element in link function.
when click on slide it's name , hidden data should append on list below it.
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 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 access 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 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 }); } };
a few things of note here:
1. if you're not creating directive in such way child directives should able require
, access controller, may consider using link function instead of controller. $timeout
dependency can moved directive factory function.
2. directives sharing scope; since directives weren't told create new or isolate scope, respective scope.selected
properties (a function in 1 , value in other) overwriting each other.
an isolate scope fixes problem, can't scope.$broadcast
scopes no longer connected. choices are
- broadcast event on parent scope:
scope.$parent.$broadcast
- broadcast event on
$rootscope
(which ultimate parent of scopes) - use shared service instead of event broadcasts (this do)
3. if @ the documentation scope#$on
, you'll see first argument listener function event triggered; second argument custom data sent $broadcast
function.
4. in 1.1.x versions of angular, can't have identical data in ng-repeat
attribute without adding track by
clause tell angular data should use determine if data duplicate. here use $index
:
<li ng-repeat="selection in selected track $index">{{ selection }}</li>
addressing these issues gets code: http://jsfiddle.net/binarymuse/hcdja/; problem ng-click
still being eaten jquery plugin. class of issue isn't uncommon when working third-party jquery plugins in angular, , answer write directive wrap plugin's functionality.
after bit of effort, have set of directives wrap swiper's functionality (at least bit care about; swiper has pretty wide surface area in terms of api, didn't cover all) in reusable way. had hard time getting setdata
, getdata
work correctly (i suspect it's bug in plugin) ended hacking way around regular data()
calls , external object store callbacks.
before code, can see working demo here: http://jsfiddle.net/binarymuse/urung/
here's final 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 $index">{{item | json}}</li> </ul> </div> </div>
i've split off swiper
, slide
elements make them reusable , composable; slide
directive uses require
attribute @ controller defined parent swiper
directive access function exposes.
here's javascript make work:
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, // use controller here slide directive // can require , call `addslide`. controller: function($element) { var newslides = []; var myswiper = null; var slidecount = 0; var callbacks = {}; // attached directly controller other directives // have access it. this.addslide = function(html, callback) { if (myswiper) { var newslide = myswiper.createslide(html.html()); // hackily save off callback based on // unique id since getdata() // swiper.clickedslide doesn't appear 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 // slide off in array can add later. newslides.push({html: html, callback: callback}); } }; $timeout(function() { myswiper = $element.swiper({ mode: 'horizontal', loop: true, onslideclick: function(swiper) { // callback saved off , call it. var clicked = swiper.clickedslide; var slidenumber = clicked.data('slidenumber'); var callback = callbacks[slidenumber]; if (callback) callback(); } }); // myswiper has been initialized, iterate // on calls `addslide` happened // before ready , add them swiper. (var = 0; < newslides.length; i++) { var slide = newslides[i]; this.addslide(slide.html, slide.callback); } }.bind(this)); } } }) .directive('slide', function() { return { restrict: 'ea', // parent `swiper` element , 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 me", "please"]; $scope.select = function(slide) { $scope.items.push(slide); }; });
you can see we've managed keep swiper-specific functionality directives, while data we're looping on (slides
) , callback fire (select
) attached controller scope, make more sense (as application-specific data).
again, working demonstration can found here: http://jsfiddle.net/binarymuse/urung/
Comments
Post a Comment