2013-07-17 88 views
2

试图让3D党jQuery库有两个非常简单的自定义指令的工作:AngularJS - 指令,NG-重复,NG-点击和jQuery

无法获得纳克单击工作,不知道如何从链接函数中的重复元素中获取数据。

当你点击一张幻灯片时,它的名字和隐藏的数据应该附加在它下面的列表中。

jsfiddle

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

当您单击应该在哪里发生,那该多好? –

+0

更新了你的问题 –

回答

4

注意到这里的几件事情只是使用链接功能而不是控制器。可以将$timeout依赖关系移至指令工厂功能。

2.您的指令共享范围;由于指令并未被告知创建新的或隔离的范围,因此它们各自的属性(函数中的一个函数和另一个中的值)相互覆盖。

隔离范围解决了这个问题,但是因为范围不再连接,所以您不能执行scope.$broadcast。您的选择是

  1. 广播父范围的事件:scope.$parent.$broadcast
  2. 广播上$rootScope事件(这是所有范围内的最终母公司)
  3. 使用共享服务,而不是事件广播(此大概是我会做的)

3。如果你看看the documentation for Scope#$on,你会发现监听器函数的第一个参数是被触发的事件; 的参数将您的自定义数据发送到$broadcast函数中。

4.在角1.1.x的版本中,你不能在ng-repeat属性相同的数据,而无需添加track by子句告诉角哪些数据应该用它来确定数据是否真的重复。这里我们使用$index

<li ng-repeat="selection in selected track by $index">{{ selection }}</li> 

解决这些问题,使我们向这个代码:http://jsfiddle.net/BinaryMuse/hCdJA/;问题在于ng-click仍然被jQuery插件所占用。在Angular中使用第三方jQuery插件时,这类问题并不罕见,答案通常是编写一个指令来封装插件的功能。


努力一点后,我有一组指令,使包装组队,探索的功能(至少是位我们所关心的;组队,探索在API方面有相当广泛的表面积,所以我没有覆盖这一切)以一种相当可重用的方式。我很难让setDatagetData正常工作(我怀疑它是插件中的一个错误),所以最终以常规的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> 

我已经分裂出去的swiperslide元素,使它们可重复使用和可组合的; 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); 
    }; 
}); 

你可以看到,我们已经成功地让所有的具体组队,探索的功能的指令,而数据我们遍历(slides)并且触发回调(select)附加到控制器作用域,它们更有意义(因为它们是应用程序特定的数据)。

此外,工作演示可以在这里找到:http://jsfiddle.net/BinaryMuse/UruNG/

+0

非常感谢,非常感谢。 –

+0

没问题;如果您还有其他问题,请随时[ping me](http://brandontilley.com/contact.html)。 –

1

我注意到两件事:

1)你不需要逻辑函数都没有。当你使用模板时,angular会负责编译模板内的指令。此外,您的链接函数绑定到选择器元素,而不是每个li,因此您无法确定哪个数据对象被点击。

2)您的指令使用继承的范围,但都将不同的事物分配给相同的属性名称。在选择器指令中,您将$scope.selected分配为一个函数。在选定的指令中,您将$scope.selected指定为值的数组。这些干扰因为他们使用相同的范围。我能够通过将第一个更改为$scope.select = function(data)...来修复它。

3)你写了你的事件处理程序来查找数据作为第一个参数。该事件是第一个参数,之后的任何参数绑定到广播事件时传递的参数,所以它会更好,如$scope.$on('slideSelected', function(event, data)...

4)您的事件处理程序不需要应用范围,因为这将自动发生,而只是更新模型。

更新的小提琴是here

1.如果你不这样认为孩子的指令应该能够require它并访问其控制器创建指令,你可以考虑: