angular.js ng-repeat 用于创建网格
Posted
技术标签:
【中文标题】angular.js ng-repeat 用于创建网格【英文标题】:angular.js ng-repeat for creating grid 【发布时间】:2013-12-06 21:33:57 【问题描述】:我正在尝试使用 bootstrap 3 和 angularjs 创建一个网格。
我正在尝试创建的网格是这样的,使用 ng-repeat 重复。
<div class="row">
<div class="col-md-4">item</div>
<div class="col-md-4">item</div>
<div class="col-md-4">item</div>
</div>
我尝试使用ng-if
和($index % 3 == 0)
来添加行,但这似乎无法正常工作。任何建议都会很棒!
谢谢!
编辑:这是我最终使用的代码:
<div ng-repeat="item in items">
<div ng-class="row|($index % 3 == 0)">
<ng-include class="col-sm-4" src="'views/items/item'"></ng-include>
</div>
</div>
【问题讨论】:
如果您将plnkr.co 与当前代码放在一起会更好 提供的解决方案似乎效果不佳。我在下面添加了一个答案,其中包含两种使用平面列表制作网格的技术。 plunker 这里。 【参考方案1】:公认的答案是显而易见的解决方案,但是表示逻辑应该保留在视图中,而不是在控制器或模型中。我也无法让 OP 的解决方案发挥作用。
当您有一个平面列表(数组)项目时,这里有两种创建网格系统的方法。 假设我们的项目列表是一个字母表:
Plunker here
$scope.alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
方法一:
这是一个纯角度解决方案。
<div class="row" ng-repeat="letter in alphabet track by $index" ng-if="$index % 4 == 0">
<div class="col-xs-3 letter-box"
ng-repeat="i in [$index, $index + 1, $index + 2, $index + 3]"
ng-if="alphabet[i] != null">
<div>Letter i + 1 is: <b> alphabet[i]</b></div>
</div>
</div>
外循环在每 4 次迭代后执行并创建一行。对于外循环的每次运行,内循环迭代 4 次并创建列。由于无论数组中是否有元素,内循环都会运行 4 次,ng-if
确保如果数组在内循环完成之前结束,则不会创建无关的列。
方法二:
这是一个更简单的解决方案,但需要 angular-filter 库。
<div class="row" ng-repeat="letters in alphabet | chunkBy:4">
<div class="col-xs-3 letter-box" ng-repeat="letter in letters" >
<div>Letter $index + 1 is: <b> letter</b></div>
</div>
</div>
外部循环创建 4 个字母组成的组,对应于我们的“行”
[['A', 'B', 'C', 'D'], ['E', 'F', 'G', 'H'], ... ]
内部循环遍历组并创建列。
注意:方法 2 可能需要为外循环的每次迭代评估过滤器,因此方法 2 可能无法很好地适应大型数据集。
【讨论】:
这比我的回答要好得多(我真的同意将演示逻辑保留在控制器之外,如果不这样做会变得非常混乱)。此外,即使您不使用 angular-filter,方法 2 中的过滤器也应该很容易自己编写。 @Clawish,如果您想要 3 列,那么您还需要将列类更改为 col-xs-4。 bootstrap 中有 12 列,因此 3 列意味着每列占用 4 个块,因此 col-xs-4。如果将其保持为 col-xs-3 不变,则组合仍然会发生,但列只是“包裹”起来,不会产生视觉差异。检查plnkr.co/edit/L207RC5Dmxk61fusC2rr?p=preview 好的我找到了解决办法,你需要使用'as':item in items | filter:x as results
@see docs.angularjs.org/api/ng/directive/ngRepeat
@user1943442,您能否详细说明方法 2 不起作用的原因? plunker 中的代码正在运行
请注意,groupBy 现在是 chunkBy: n in angular-filter【参考方案2】:
这是一个旧答案!
当我写这篇文章时,我对 Angular 还是有点陌生。 Shivam 下面有一个更好的答案,我建议您改用它。它将表示逻辑排除在控制器之外,这是一件非常好的事情。
原答案
您始终可以在控制器中将要重复的列表拆分为列表列表(每个列表包含三个项目)。所以你列出的是:
$scope.split_items = [['item1', 'item2', 'item3'], ['item4', 'item5', 'item6']];
然后重复如下:
<div ng-repeat="items in split_items" class="row">
<div ng-repeat="item in items" class="col-md-4">
item
</div>
</div>
不确定是否有更好的方法。我也尝试过使用 ng-if 和 ng-switch,但我永远无法让它工作。
【讨论】:
埃里克,谢谢。我实际上找到了添加到原始帖子中的解决方案。不过,我会继续接受你的工作替代方案 您找到的解决方案实际上并没有按照您的意愿执行。它只是将每三个元素包装在一行中,而不是包装所有三个元素。此外,这种编写 ng-class 的方式似乎也不起作用,但也许我们正在使用不同版本的 angular。 我认为内部 div 中的“项目”需要用双花括号括起来,不是吗? item 是的,“项目”只是我说“把你的东西放在这里”的方式。可能不应该使用与我在实际代码中使用的关键字相同的关键字 :) 无论如何,您应该查看 Shivam 的答案,它会更好,因为它适用于平面列表并将表示逻辑排除在控制器之外!跨度> 【参考方案3】:您可以在控制器内部简单地将数组分块为 N 的子数组。示例代码:
var array = ['A','B','C','D','E','F','G','H'];
var chunk = function(arr, size)
var newArr = [];
for (var i=0; i<arr.length; i+=size)
newArr.push(arr.slice(i, i+size));
return newArr;
;
$scope.array = chunk(array, 2);
现在在 *.html 文件中,您只需通过 array
ng-repeat
即可
<div class="row" ng-repeat="chunk in array">
<div class="col-md-6" ng-repeat="item in chunk">
item
</div>
</div>
对我的锻炼 :) 祝你好运!
【讨论】:
我喜欢这种方法!【参考方案4】:有人可能会说下面的解决方案不遵循具有row
div 的网格规则,但另一种解决方案是删除row
类(或在ng-repeat
之外使用它)并使用@改为 987654324@ 类:
<div class="col-md-12">
<div ng-repeat="item in items">
<div ng-class="'clearfix': $index%3 === 0"></div>
<div class="col-md-4">item</div>
</div>
</div>
据我所知,这看起来与 row
类几乎相同,但请评论可能存在的缺陷(除了我上面提到的那个)。
【讨论】:
聪明、肮脏、富有感情。 +1【参考方案5】:使用 ng-repeat-start 和 ng-repeat-end
<div class="row">
<div ng-repeat-start="item in items track by $index" class="col-sm-4">
item
</div>
<div ng-repeat-end ng-if="($index+1) % 3 == 0" class="clearfix"></div>
</div>
使用 .visible-* 类可以轻松适应不同的媒体查询
<div class="row">
<div ng-repeat-start="item in items track by $index" class="col-lg-2 col-md-4 col-sm-6">
item
</div>
<div ng-repeat-end>
<div class="clearfix visible-lg-block" ng-if="($index+1) % 6 == 0"></div>
<div class="clearfix visible-md-block" ng-if="($index+1) % 3 == 0"></div>
<div class="clearfix visible-sm-block" ng-if="($index+1) % 2 == 0"></div>
</div>
</div>
我发现在主重复块之外具有行管理逻辑非常简洁明了。关注点分离:-)
【讨论】:
谢谢,经过几个小时的研究,第一个对我来说非常有效的解决方案。【参考方案6】:答案有点晚,但我使用了这个,我相信它在某些情况下会更好。您可以为此使用 Angular Filter 包及其 ChunkBy 过滤器。虽然这个包对于这个单一的任务来说是一项繁重的工作,但它还有其他有用的过滤器来完成不同的任务。我使用的代码是这样的:
<div class="row mar-t2" ng-repeat="items in posts | chunkBy:3">
<div class="col-md-4" ng-repeat="post in items">
<img ng-src="post.featured_url" class="img-responsive" />
<a ng-click="modalPop(post.id)"><h1 class="s04-bas2">post.title.rendered</h1></a>
<div class="s04-spotbox2" ng-bind-html="post.excerpt.rendered"></div>
</div>
</div>
【讨论】:
【参考方案7】:我使用ngInit 采用了一种稍微不同的方法。我不确定这是否是合适的解决方案,因为 ngInit 文档指出
ngInit 的唯一适当用途是为 ngRepeat 的特殊属性设置别名,如下面的演示所示。除了这种情况,您应该使用控制器而不是 ngInit 来初始化作用域上的值。
我不确定这是否属于这种情况,但我想将此功能从控制器中移开,以便模板设计人员更轻松地使用引导程序按行分组。我还没有测试这个绑定,但是看到我正在跟踪 $index 我不认为这应该是一个问题。
希望听到反馈。
我创建了一个名为“splitrow”的过滤器,它接受一个参数(每行中有多少项的计数)
.filter('splitrow', function()
return function (input, count)
var out = [];
if(typeof input === "object")
for (var i=0, j=input.length; i < j; i+=count)
out.push(input.slice(i, i+count));
return out;
);
在视图模板中,我按如下方式组织引导行:
<div ng-init="rows = (items|splitrow:3)">
<div ng-repeat='row in rows' class="row">
<div ng-repeat="item in row track by $index" class="col-md-4">
item.property
</div>
</div>
</div>
我编辑了@Shivam 的 Plunker 来使用这种方法。它不需要外部库。
Plunker
【讨论】:
我喜欢这个解决方案。但是,我无法让它处理来自工厂的数据,因为ng-init
首先运行并且数据还没有准备好。如果数据是 $scope 上的简单数组,它可以正常工作。有什么想法吗?
@WonderBred 你能在 plunker 中展示一个它不起作用的例子吗?我让它在我的应用程序上与一家工厂合作,该应用程序仍在开发中。我只在 2 个地方真正使用它,所以我没有遇到指令在数据之前加载的任何问题。
我会尝试找到一个使用 $http
的 plunker。我有一种感觉,这就是为什么它不起作用。您在工厂使用$http
吗?感谢您的回复。
@WonderBred 我在工厂使用 $resource。但仍然不确定这是否会有所作为。对您的用例非常感兴趣。我还是 Angular 的新手。
这是一个说明我遇到的问题的代码笔。它适用于硬编码数组,但不适用于工厂的数据。顺便说一句,以后有什么方法可以更改计数吗?例如,我想在某个时候触发一个调整大小的函数来改变列数。我不确定 ng-init 是否可以做到这一点。这是codepen。您可以取消注释数组以查看过滤器是否正常工作。 codepen.io/mdmoore/pen/Qbqmoo?editors=101【参考方案8】:
我的解决方案与@CodeExpress 非常相似。我制作了一个batch
过滤器,它对数组的项目进行分组(名称是从Twig's counterpart filter 借来的)。
为简单起见,我不处理关联数组。
angular.module('myapp.filters', [])
.filter('batch', function()
var cacheInputs = [];
var cacheResults = [];
return function(input, size)
var index = cacheInputs.indexOf(input);
if (index !== -1)
return cacheResults[index];
var result = [];
for (i = 0; i < input.length; i += size)
result.push(input.slice(i, i + size));
cacheInputs.push(input);
cacheResults.push(result);
return result;
)
;
可以这样使用:
<div ng-repeat="itemsRow in items|batch:3">
<div class="row">
<div ng-repeat="item in itemsRow">
<div class="col-md-4">
...
</div>
</div>
</div>
</div>
过滤结果被缓存以避免10 $digest() iterations reached. Aborting!
错误。
【讨论】:
我刚刚犯了一个错误rejecting@EmmanuelVerlynde 对这个答案的建议编辑。当editing his own post @MichaëlPerrin 错过了将变量cache
替换为cacheInputs
时。我刚刚做了一个编辑来纠正这个问题。无论如何,感谢@EmmanuelVerlynde 发现这个错误。
糟糕,抱歉,我忘记在各处重命名我的变量了; cacheInputs
确实应该使用。谢谢@EmmanuelVerlynde 和@altocumulus!【参考方案9】:
@CodeExpress 的纯角度解决方案的 Angular2 版本。
alphabet: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
<div *ngIf='alphabet && alphabet.length'>
<div *ngFor='#letter of alphabet; #i = index' >
<div class="row" *ngIf='i % 4 == 0'>
<div class="col-md-3" *ngFor='#n of [i,i+1,i+2,i+3]'>
alphabet[n]
</div>
</div>
</div>
</div>
【讨论】:
【参考方案10】:这应该可行
<div ng-repeat="item in items">
<div ng-class="row : ($index % 3 == 0)">
...
</div>
</div>
【讨论】:
【参考方案11】:Angular9 更新:
<div class="container-fluid" *ngFor="let item of alphabet; let index = index">
<div class="row" *ngIf="index % 4 == 0">
<div *ngFor="let i of [index, index+1, index+2, index+3]">
<div class="col-xs-3">alphabet[i]</div>
</div>
</div>
</div>
【讨论】:
以上是关于angular.js ng-repeat 用于创建网格的主要内容,如果未能解决你的问题,请参考以下文章
折叠使用 Angular js ng-repeat 和过滤器创建的 div
如何在angular js中的重复中间停止ng-repeat
Angular JS——将ng-repeat应用于异步添加到DOM的东西