【问题标题】:Using slick caroussel inside a template在模板中使用光滑的轮播
【发布时间】:2014-09-02 16:58:03
【问题描述】:

我们的客户要求我们实施 kenWheeler 光滑轮播:http://kenwheeler.github.io/slick/

由于我们的项目是使用 AngularJs 构建的,我们发现以下指令可以作为插件的包装器。 https://github.com/vasyabigi/angular-slick

该指令在模板内调用如下:

<slick dots="true" slides-to-show=3>
    <div ng-repeat="index in question.getProperty().getDynamicData().getIndexes()">
        <span>
            <img ng-src="{{builtPathToImage(question.getProperty().getDynamicData().getImage($index))}}">
        </span>
        <span>{{question.getProperty().getDynamicData().getLabel($index)}}</span>
    </div>
</slick>

当在代码中调用模板时,div 会即时创建。指令运行,div 相互叠加显示。

编辑:

  • carroussel 内的div 是在页面加载后创建的

根据我的研究,问题可能在于页面加载时指令为“空”这一事实。

Plunkr : http://plnkr.co/edit/EdKT8W?p=preview 点击“搞砸”按钮重新创建问题。

编辑结束

关于上下文的更多信息: 该项目使用 require.js 角度引导

有没有人在模板中使用过这个指令?如果是这样,您能否提供一些关于您如何使其工作的指针。

谢谢

【问题讨论】:

  • 没有答案,但是……carousels are evil
  • 好文章。我们希望以手动方式使用它。用户必须手动滑动转盘。
  • @DavidLaberge 你能提供一个小提琴或 plunkr 吗?我几乎可以保证我可以解决你的问题。
  • @WillemD'haeseleer 按要求添加了插件
  • @DavidLaberge 您似乎在新资源中缺少http 方案。是不是打错字了?

标签: angularjs angularjs-directive


【解决方案1】:

发生这种情况的原因是因为插件本身极大地修改了由ngRepeat 生成的 html,导致 angular 无法更新转发器,因为它不再能够识别结构。

为了防止 angular 在更新中继器时混淆,您可以:

  • 取消初始化插件
  • 更新中继器
  • 重新初始化插件。

我在这里有一个工作示例:

http://plnkr.co/edit/ZQrDZ3Nr33S95Y4uy1AZ?p=preview

它通过添加 slickApply 方法对 angular-slick.js 包装器稍作修改,以便您可以在控制器中轻松完成此操作。

变化:

angular-slick.js

    touchThreshold: '@',
    vertical: '@',
    slickApply: '='  // this is the two way binding that changes the interface
  },
  link: function (scope, element, attrs) {
    var initializeSlick, isInitialized;
    var slider; // slider has been moved from the initializeSlick function to here
    /* 
      This function takes another function as an argument 
       and will reset the slick plugin, call the function, and reload it
    */
    scope.slickApply = function(apply){
        if (isInitialized) {
            slider.unslick();
        }
        apply();
        initializeSlick();
    }
    initializeSlick = function () {
      return $timeout(function () {
        var currentIndex;
        slider = $(element);

index.html

这里唯一改变的是添加了slick-apply 属性,它引用了$scope 中的slickApply 函数

<h1>Hello Plunker!</h1>
<slick init-onload="false" slick-apply='slickApply' data="dataLoaded" slides-to-show="3" dots="true">
  <div ng-repeat="item in items">
    <span>

script.js

这里唯一改变的是将$scope.items 的赋值封装到$scope.slickApply 函数中。

$scope.messItUp = function(){
    $scope.slickApply(function(){
      $scope.items = [
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 1'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 2'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 3'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 4'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 5'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 6'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 7'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 8'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 9'},
          {imgSrc: 'http://lorempixel.com/325/325/sports/', label: 'label 10'}
      ]  
    });
}

您也可以在不修改包装器的情况下完成所有这些操作,但这需要您在控制器中执行笨拙的 jQuery / angular 操作,正如 miron 所示:

http://plnkr.co/edit/WCEWwgNcIEC0rseaZIO6?p=preview

我认为您绝对可以改进前一个示例,特别是通过修改包装器,使其能够识别角度集合,目前它不这样做。但这显然比我在这里提供的热修复要多得多。

如果您打算在整个代码中更多地使用这个 slick 库,我绝对建议您 fork git 存储库,以便您可以以更易于管理的方式对其进行任何其他更改。

但是,由于整个 包装器 实际上是 1 个文件,其中甚至没有 100 行代码,我认为直接在您的代码库中采用它并没有任何问题。
但是,如果您想合并上游更改,我建议将其保留为咖啡脚本。但是编译后的输出看起来并不可怕,所以我猜它并不重要。

【讨论】:

  • 我希望能更准确地编辑问题。我认为该指令在加载页面之前填充 HTML 时有效。
  • 可能是一种可能的方法,但请记住,您需要修改包装器代码,这有时不是最佳解决方案。
  • 这个包装代码非常有限,并没有真正添加任何功能。修改它似乎是最好的做法。
  • 如果在生产站点上您依赖于任何前端依赖系统,例如 bower,那么我建议您提出拉取请求 :) 为了完整起见,您还应该在 unInitializeSlick 方法中将变量 isInitialized 设置为 false。
  • 这个包装器非常有限,不得不通过拉取请求的繁文缛节在这里似乎过分了,我只会获得回购的所有权或直接将其提交到代码库。它没有许可证,所以那里没有问题。
【解决方案2】:

我不知道你的数据结构是什么样的,但我试图尽可能重现你的问题。

我简化了数据,我只使用包含任何特定数据的对象,例如图像源和标签文本:

$scope.items = [
  {
    imgSrc: 'http://placekitten.com/325/325',
    label: 'label 1'
  }
];

在循环中,我只使用了 ng-repeat 迭代的当前项目的属性:

<div ng-repeat="item in items">
  <span>
    <img ng-src="{{ item.imgSrc }}" alt="{{ item.label}}">
   </span>
   <span>{{ item.label }}</span>
</div>

但是现在是您的主要问题。我可以通过延迟加载的数据来重新创建它。我将数据分配包装在超时块中。我使用 init-onload 和 data 属性扩展了 slick 元素:

<slick init-onload="false" data="dataLoaded" slides-to-show="3" dots="true"></slick>

现在,当数据完全加载后,dataLoaded 为真,并且轮播被渲染。

我不知道为什么,但重要的是 dataLoaded 变量不会被初始化为 $scope.dataLoaded = false; 这样的初始值,因为它不再起作用。

编辑:

因此,就像您 can see 一样,光滑的轮播不会将 reinit 方法暴露给公共 api,因此即使元素发生变化,您也必须欺骗它以保持轮播工作:

<slick ng-if="dataLoaded" init-onload="false" data="dataLoaded" slides-to-show="3" dots="true"></slick>

ng-if 删除了 slick 元素的完整 dom 结构,所以这正是我们需要的。现在,当您更新数据时,您只需将$scope.dataLoaded 赋值包装在超时函数中,以将其放入事件循环中。因此 Angular 有时间删除创建的旧光滑轮播结构:

$scope.dataLoaded = false;

$timeout(function(){
  $scope.dataLoaded = true;
});

I prepared an working plunk for you. Let me know if it solves your problem.

【讨论】:

  • 感谢您的鼓励。如果您添加一个按钮来更改轮播中断范围内的 url,问题仍然存在。 plnkr.co/edit/EdKT8W?p=preview
  • 我查看了 slick carousel 的公共 api,它没有公开 reinit 方法来重新绘制 carousel,因此我们需要欺骗它来重新创建 slick 元素。我更新了我的 plunk,所以看看:plnkr.co/edit/WCEWwgNcIEC0rseaZIO6?p=preview
  • 这会重新初始化整个指令,并要求您通过 $timeout 切换魔法标志,我不喜欢这种方法
  • 谢谢@miron 我目前正在尝试您的解决方案,而 willem 的解决方案,一旦在我的上下文中工作,我会尽快回复您。感谢您的所有努力
猜你喜欢
  • 2017-08-13
  • 2015-08-11
  • 1970-01-01
  • 2016-01-26
  • 1970-01-01
  • 1970-01-01
  • 2022-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多