【问题标题】:angular.js ng-repeat for creating gridangular.js ng-repeat 用于创建网格
【发布时间】: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 这里。

标签: javascript angularjs twitter-bootstrap


【解决方案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>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-05-11
          • 1970-01-01
          • 2016-03-03
          • 2012-10-10
          • 2013-05-29
          • 2023-04-07
          相关资源
          最近更新 更多