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 文件中,您只需通过 arrayng-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-startng-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中ng-repeat是啥意思

Angular JS——将ng-repeat应用于异步添加到DOM的东西

如何在 angular.js 中解析 ng-repeat 中的 HTML [重复]

Angular.js ng-repeat 跨多个元素