Ng-Repeat array to rows and columns

2020-05-23 05:18发布

问题:

Thanks for taking the time to read this, I was wondering how I might be able to use ng-repeat to create a grid like box of options. I would like to take an array repeat nth number of items and then move to the next row or column until all items are listed. e.g.

assuming I had an array like [opt1,opt2,opt3,opt4,opt5,opt6,opt7] I would like to display it like this:

opt1 opt2 opt3
opt4 opt5 opt6
opt7

回答1:

This is more a styling/markup problem than an AngularJS one. If you really want to, you can do:

<span ng:repeat="(index, value) in array">
    {{value}}<br ng:show="(index+1)%3==0" />
</span>

http://jsfiddle.net/JG3A5/



回答2:

Sorry for my HAML and Bootstrap3:

.row
  .col-lg-4
    %div{'ng:repeat' => "item in array.slice(0, array.length / 3)"}
      {{item}}
  .col-lg-4
    %div{'ng:repeat' => "item in array.slice(array.length / 3, array.length * 2/3)"}
      {{item}}
  .col-lg-4
    %div{'ng:repeat' => "item in array.slice(array.length * 2/3, array.length)"}
      {{item}}

There is another version, with possibility to use filters:

<div class="row">
  <div class="col-md-4" ng-repeat="remainder in [0,1,2]">
    <span ng-repeat="item in array" ng-if="$index % 3 == remainder">{{item}}</span>
  </div>
</div>


回答3:

If all of your items are in one single array, your best bet is to make a grid in CSS. This article should be helpful: http://css-tricks.com/dont-overthink-it-grids/

You can use $index from ng-repeat to apply the correct class for your column (in this case a 4 column grid):

<div class="col-{{ $index % 4 }}"></div>

If you have a 2 dimensional array (split into rows and columns) that opens up more possibilities like actually using an HTML table.



回答4:

I find it easier to simply use ng-repeat combined with ng-if and offsetting any indexes using $index. Mind the jade below:

div(ng-repeat="product in products")
    div.row(ng-if="$index % 2 === 0")
      div.col(ng-init="p1 = products[$index]")
          span p1.Title
      div.col(ng-if="products.length > $index + 1", ng-init="p2 = products[$index + 1]")
          span p2.Title
      div.col(ng-if="products.length <= $index + 1")


回答5:

Between Performance, Dynamics and Readability

It seems putting the logic in your JavaScript is the best method. I would just bite-the-bullet and look into:

function listToMatrix(list, n) {
    var grid = [], i = 0, x = list.length, col, row = -1;
    for (var i = 0; i < x; i++) {
        col = i % n;
        if (col === 0) {
            grid[++row] = [];
        }
        grid[row][col] = list[i];
    }
    return grid;
}

var matrix = listToMatrix(lists, 3);
console.log('#RedPill', matrix);
  • @ Params: (list, n)
    • Where list is any array and n is an arbitrary number of columns desired per row
  • @ Return: A matroid
  • @ Note: This function is designed to orient a matroid based upon an arbitrary number of columns with variance in its number of rows. In other words, x = desired-columns, y = n.

You can then create an angular filter to handle this:

Filter:

angular.module('lists', []).filter('matrical', function() {
    return function(list, columns) {
        return listToMatrix(list, columns);
    };
});

Controller:

function listOfListsController($scope) {
    $scope.lists = $http.get('/lists');
}

View:

<div class="row" ng-repeat="row in (lists | matrical:3)">
    <div class="col col-33" ng-repeat="list in row">{{list.name}}</div>
</div>

With this, you can see you get n number of rows -- each containing "3" columns. When you change the number of desired columns, you'll notice the number of rows changes accordingly (assuming the list-length is always the same ;)).

Here's a fiddle.

Note, that you get the ol' Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!. This is because Angular is recalling the matrical function upon every iteration. Allegedly, you can use the as results alias to prevent Angular from reevaluating the collection, but I had no luck. For this, it may be better to filter the grid inside of your controller and use that value for your repeater: $filter('matrical')(items) -- but please post back if you come across an elegant way of filtering it in the ng-repeat.

I would stress, again, you're probably heading down a dark alley by trying to write the logic in your view -- but I encourage you to try it in your view if you haven't already.

Edit

The use of this algorithm should be combined with a Matrical Data-Structure to provide methods of push, pop, splice, and additional methods -- in tandem with appropriate logic to complement Bi-Directional Data-Binding if desired. In other words, data-binding will not work out of the box (of course) as when a new item is added to your list, a reevaluation of the entire list must take place to keep the matrix's structural integrity.

Suggestion: Use the $filter('matrical')($scope.list) syntax in combination with $scope.$watch and recompile/calculate item-positions for the matrix.

Cheers!