【问题标题】:Refresh Bootstrap ScrollSpy after Angular model changesAngular 模型更改后刷新 Bootstrap ScrollSpy
【发布时间】:2013-01-11 18:18:24
【问题描述】:

我正在尝试使用bootstrap scollspy 突出显示由角度中继器生成的列表项。

我遇到的问题是,在 angular 将模型更改应用于视图之前,我正在从 angular 控制器刷新 scrollspy 插件。

确保在 DOM 本身更新后发生 scrollspy('refresh') 调用的角度方式是什么(不仅仅是角度模型)?

模板:

<div class="span2" id="addressList">
    <ul class="nav nav-tabs nav-stacked affix">
       <li ng-repeat="addr in addresses"><a href="#{{addr.id}}">{{addr.id}}</a></li>
    </ul>
</div>

控制器:

$scope.httpSuccessCallback = function (data) 
     $scope.addresses.push(data);
     $('[data-spy="scroll"]').scrollspy('refresh'); //calls $('#addressList .nav > li > a')
 }

【问题讨论】:

  • 这可能已经说了 1000 次了。但是你不想从你的控制器函数中进行 DOM 操作。您要做的是创建一个处理所有这些的指令。然后在触发更新的任何东西上使用 $watch 来更新它。
  • 我没有在我的控制器中进行任何 DOM 操作。我正在尝试在角度生命周期的适当位置更新 jQuery 插件。似乎 $watch 可能仍然是正确的机制?
  • 您正在从 DOM 中选择一个元素,并绑定事件等... 这就是我所说的“DOM 操作”的真正含义。 Angular 文档很好地解决了这个问题。您不应该在控制器中引用 DOM。
  • 这里,请参阅“正确使用控制器”部分:docs.angularjs.org/guide/dev_guide.mvc.understanding_controller

标签: jquery-plugins twitter-bootstrap angularjs


【解决方案1】:

在不了解滚动间谍任何的情况下,以下是您通常希望在 Angular 中使用 JQuery 插件的方式:

app.directive('scrollSpy', function (){
   return {
     restrict: 'A',
     link: function(scope, elem, attr) {
         elem.scrollSpy({ /* set up options here */ });

         //watch whatever expression was passed to the
         //scroll-spy attribute, and refresh scroll spy when it changes.
         scope.$watch(attr.scrollSpy, function(value) {
              elem.scrollSpy('refresh');
         });
     }
   };
});

然后在 HTML 中:

<div scroll-spy="foo">Do something with: {{foo}}</div>

上面的例子非常通用,但它基本上会将你的插件应用到一个元素上,并在每次 $scope.foo 更改时调用'refresh'或其他任何东西。

希望对你有帮助。

【讨论】:

  • 那么当 $watch 触发时,是否保证视图已经被渲染并且 DOM 操作完成并且对象的更改生效?必须先更新 DOM,然后才能刷新。
  • 执行此操作时出现无法识别的表达式错误。我正在使用模型来更新锚点 (href="#category{{cat.id}}"),我认为 $watch 回调会触发 before Angular 更新 DOM。 Bootstrap 滚动插件尝试使用尚未解决的#category{{cat.id}},并给出错误。
  • 角度文档提到了指令声明的 compile() 函数中可用的前链接和后链接函数。后链接应该对 DOM 工作是安全的。我还不确定如何使用它。 docs.angularjs.org/guide/directive
  • 当然,请参阅“编写指令(长版)”in the documentation here
  • @blesh - 在我的示例中,$scope.addresses 是一个数组,我将值从文本输入中推入。在指令中,当我修改 $scope.addresses 时,我的 scope.$watch('addresses', ...) 调用永远不会触发。当我将代码更改为 scope.$watch('myTextInput',...) 时,它会在我修改文本输入时触发。这是 $watch 函数的隐含限制吗?
【解决方案2】:

我是如何使用 Blesh 的回答解决这个问题的

模板:

  <body ng-app="address" ng-controller="AddressCtrl" scroll-spy="addresses">
    <div class="container">
      <form ng-submit="lookupAddress()">
        <input type="text" ng-model="addressText" />
        <button id="addressQueryBtn">Submit</button>
      </form>

      <div id="addressList">
        <ul>
          <li ng-repeat="addr in addresses">{{addr}}</li>
       </ul>
      </div>

    </div>
  </body>

Angular JS:

angular.module('address', []).
directive('scrollSpy', function($timeout){
  return function(scope, elem, attr) {
    scope.$watch(attr.scrollSpy, function(value) {
      $timeout(function() { elem.scrollspy('refresh') }, 200);
    }, true);
  }
});

function AddressCtrl($scope, $http) {
  $scope.addresses = [];

  $scope.lookupAddress = function() {
    $scope.addresses.push($scope.addressText);
    $scope.addressText = '';
  };
}

当被监视的作用域变量是一个对象或数组时,需要使用 scope.watch(...) 的第三个参数。不幸的是,这个解决方案仍然会导致在 cmets 中 randomguy 提到的无法识别的表达式问题。我最终通过手表功能中的using a timeout 解决了这个问题。

【讨论】:

    【解决方案3】:

    在 Angular 中使用 Scrollspy 存在一些挑战(这可能是 AngularUI 到 2013 年 8 月仍未包含 Scrollspy 的原因)。

    1. 您必须在对 DOM 内容进行任何更改时刷新滚动间谍。

      正如这里所建议的,这可以通过 $watching 元素并为 scrollspy('refresh') 调用设置超时来完成。

    2. 如果您希望能够使用导航元素来导航可滚动区域,则需要覆盖导航元素的默认行为。

    这可以通过使用 preventDefault 来阻止导航并附加一个 scrollTo 函数来实现。

    我在 Plunker 上抛出了一个工作示例:http://plnkr.co/edit/R0a4nJi3tBUBsluBJUo2?p=preview

    【讨论】:

      【解决方案4】:

      我一直在寻找解决此问题的方法,但找不到。我最终通过使用一些指令创建了一个滚动间谍服务来实现我自己的。

      该服务将跟踪所有间谍部分及其偏移量。这些指令将创建一个滚动事件,突出显示导航项并告诉服务所有部分的位置。由于此实现不监视范围变量,因此应用程序需要广播消息以更新所有部分的偏移量。

      你可以在 Github 上找到我的example

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多