在 ng-repeat 中的某些元素周围添加包装器

Posted

技术标签:

【中文标题】在 ng-repeat 中的某些元素周围添加包装器【英文标题】:Adding a wrapper around some elements in an ng-repeat 【发布时间】:2015-07-10 09:26:54 【问题描述】:

可以使用ng-repeat来实现如下编译的DOM:

<div class="container">
    <!-- ngRepeat item in items -->
    <div ng-repeat="item in items">Item 1</div>
    <!-- end ngRepeat: item in items -->
    <!-- ngRepeat item in items -->
    <div ng-repeat="item in items">Item 2</div>
    <!-- end ngRepeat: item in items -->
    <!-- ngRepeat item in items -->
    <div ng-repeat="item in items">Item 3</div>
    <!-- end ngRepeat: item in items -->
    <div class="wrapper">
        <!-- ngRepeat item in items -->
        <div ng-repeat="item in items">Item 4</div>
        <!-- end ngRepeat: item in items -->
        <div ng-repeat="item in items">Item 5</div>
        <!-- end ngRepeat: item in items -->
   </div>
</div>

即将最后 n 项包裹在另一个元素中。

这似乎是一个奇怪的请求,我知道使用两个 ng-repeat 指令来实现这一点很简单。但是,它必须是单个 ng-repeat,以便我可以将项目移入和移出包装器,而无需从 DOM 中添加和删除它们(以here 所述的方式)。

我试图通过给.wrapper 元素overflow:hidden 并使用javascript 为子元素的top 位置设置动画来实现新闻提示样式的滚动效果。老实说,我宁愿根本不需要包装元素,但我不确定是否有任何其他方法可以实现我需要的滚动效果。也许操纵clip 属性来实现效果可能会奏效,但我不完全确定。

所以可以将包装元素应用于ng-repeat 中的某些项目?

【问题讨论】:

你能用 $last 来实现你想要的吗? docs.angularjs.org/api/ng/directive/ngRepeat 或检查 $index? 我确实尝试过将$indexng-if 结合起来,但没有成功。例如&lt;div class="wrapper" ng-if="$index===5"&gt;,然后是 &lt;/div ng-if="$last"&gt;,但 ng-if 指令并非设计为那样工作,也不喜欢它。 【参考方案1】:

不幸的是,当您更改可视树中元素的父级时,必须将其删除并重新添加。在其他问题中,考虑如何根据谁是谁的父母而不同地应用样式规则。在动画中这也是一件相当昂贵的事情。

令人失望?也许吧。

但是通读你的用例,我想你会发现 Angular 非常擅长处理这种动画,只需要一点点代码。我已经包含了一个小提琴供你玩:

http://jsfiddle.net/wjxgcb0k/

创建自己的布局并将其绑定到页面上的元素非常容易。我们将首先使用ng-repeat 为每个项目吐出一行:

<div class="container" ng-app ng-controller="Foo">
    <div class="item"
        ng-repeat="item in items" 
        ng-style="'top': item.top + 'px'">item.name
    </div>
</div>

因为我们要处理自己的布局,所以容器中的每个项目都是position: absolute。看看我们如何将top 绑定到item.top + 'px'?我们需要做的就是在动画循环中调整这些最高值。我将使用requestAnimationFrame,因为它是我手动制作动画的首选工具,但如果您更舒服,可以使用 css 过渡或动画。

我将初始化控制器中的顶部值。这就是它的用途,保持状态:

$scope.items.forEach(function(item, idx) 
    item.h = height;
    item.top = idx * (height + margin);
    console.log(item);
);

然后我会设置一个动画循环:

var tick = function() 
    $scope.$apply(function() 
        $scope.items.forEach(function(item, idx) 
            item.top -= velocity;
            if (item.top < -(height + margin)) 
                item.top += $scope.items.length * (height + margin);
            
        );
    );
    requestAnimationFrame(tick);
;

然后开始整件事:

requestAnimationFrame(tick);    

您可以使用它进行一些巧妙的改进:

考虑只为页面上适合的项目数量设置动画,而不是为集合中的所有项目设置动画。表现将感谢您。 不要依赖$apply 将更改传播到Dom,而是自己直接操作样式。这可以提高动画性能。 当if 条件触发并且我们将项目重置到代码的底部时,我们可能会检查是否有不同的内容要放入代码项目中。这样,您可能会拥有一个随时间变化的实时更新代码。 试着让这一切水平工作。或者当顶部接近零或列表的下半部分时调整不透明度。

我希望这会有所帮助,并且我希望执行此操作所需的少量代码将鼓励您放弃依赖 html 为您进行布局的概念。

【讨论】:

从表面上看,这似乎是最好的方法。我将对小提琴进行更详细的研究,看看是否可以使用您提供的建议获得预期的效果。感谢您指出更改可视树中的父级确实需要删除和重新添加的事实。如果滚动效果需要一个带有overflow:hidden 的容器,那么我想没有办法绕过这个事实。额外的荣誉提醒我requestAnimationFrame。动画性能在我的应用程序中至关重要,这可能会派上用场。【参考方案2】:

从技术上讲,答案是否定的,因为wrapper 存在于ngRepeat 的范围之外。因此,无法根据每个item 的属性对wrapper 设置条件。

另一方面,您仍然可以复制相同的标记。它只需要过滤和一些创造性的思考。

 <div class="container">
      <!-- items with wrapper = false are outside wrapper -->
      <div ng-repeat="item in items | 'wrapper':false">item</div>
      <div class="wrapper">
          <!-- items with wrapper = true are inside wrapper -->
          <div ng-repeat="item in items | 'wrapper':true">item</div>
      </div>
 </div>

【讨论】:

使用两个ng-repeats 时遇到的问题是,切换包装器属性以将项目移入或移出包装器将导致该项目的 DOM 在移动时被销毁并重新创建在两个ng-repeats 之间。这对于我的用例来说确实是个问题,主要问题是它无法为运动设置动画。 @djskinner 你不能在 DOM 中动画化改变父母,但我能感觉到你的痛苦。我现在正在做一个项目,使用父母给了我想要的布局,但我还需要动画父母的变化。最后,我将不得不手动设置所有内容的位置,以代表我想要的布局,而没有父母的阻碍。 谢谢,我现在明白动画父母的变化是不可能的。这是一个有用的见解。现在我想知道是否可以在不借助包装器元素的情况下实现列表中最后一个n 项目的动画滚动。我觉得可以通过为第一个和最后一个可见项目的 topclip-path 设置动画,但需要进一步调查。【参考方案3】:

我实际上不知道是否可以使用 ng-repeat 将 N 个项目包装在块内。但是可以做的是将 .wrapper 类应用于索引高于“某个值”的每个元素。这可以使用 ng-class 和 $index 来完成。

编辑: 希望我了解 new-ticker 的工作原理。如果没有,请原谅错误的答案。

plnkr

<div ng-repeat="op in options" ng-class="wrapper: $index > limit">op.title</div>

在示例中,您可以更改“限制”变量的模型值以更改可见项目的数量。

或者更好。如果包装内的项目应该“出现”,也许最简单的方法是

ng-hide="$index > end || $index < start"

这将在开始和结束时隐藏项目。操纵 'start' 和 'end' 的值会产生效果。

【讨论】:

我不确定这是否能实现我想要的,因为包装类将应用于每个项目,而我需要一个元素来包装重复中的多个(但不是全部)项目.也许代码示例可以让我更好地了解这种方法是否可行?【参考方案4】:

您能否使用 CSS 和 nth:child 访问最后两个元素来实现此目的?您可以为 ng-repeat 中的这两个节点设置可见性:隐藏。你甚至可以CSS animation with delays。

【讨论】:

以上是关于在 ng-repeat 中的某些元素周围添加包装器的主要内容,如果未能解决你的问题,请参考以下文章

在 REST API 周围添加 graphql 包装器?

text Wordpress - 在子菜单周围添加div包装器

Angular.js ng-repeat 跨多个元素

ReactJs:如何创建一个包装器组件,该组件在单击时会执行某些代码并呈现子组件?

通过在 C++ 接口周围创建 C 包装器,在 FORTRAN 中调用 C++ dll 文件 [关闭]

如何使用jQuery在元素的内部文本的最后一个单词周围包裹一个范围?