Angular.js ng-repeat для создания сетки

Я пытаюсь создать сетку, используя 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), чтобы добавить строки, но это, похоже, не работает правильно. Любые предложения были бы замечательными!

Спасибо!

EDIT: Здесь код, который я закончил, сработал:

<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>

Ответ 1

Это старый ответ!

Я все еще был немного новым в 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, но я никогда не смог заставить его работать.

Ответ 2

Принятый ответ - очевидное решение, однако логика представления должна оставаться в поле зрения, а не в контроллерах или моделях. Также я не смог заставить OP-решение работать.

Вот два способа создания сетки, когда у вас есть плоский список (массив) элементов. Скажем, наш список предметов - это алфавит:

Plunker здесь

$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'];

Способ 1:

Это чистое решение angular.

<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 гарантирует, что никакие посторонние столбцы не создаются, если массив заканчивается до завершения внутреннего цикла.

Способ 2:

Это гораздо более простое решение, но требует 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 может не очень хорошо масштабироваться для огромных наборов данных.

Ответ 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 файле. Вы просто ng-repeat через array

<div class="row" ng-repeat="chunk in array">
    <div class="col-md-6" ng-repeat="item in chunk">
         {{item}}
    </div>
</div>

Эта тренировка для меня :) Удачи!

Ответ 4

Можно сказать, что нижеприведенное решение не соответствует правилам сетки с row divs, но другим решением было бы отказаться от класса row (или использовать его вне ng-repeat) и использовать clearfix вместо:

<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, но прокомментируйте возможные недостатки (кроме тех, которые были упомянуты выше).

Ответ 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 Фильтр и его фильтр 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

Ответ 8

Мое решение очень похоже на @CodeExpress. Я создал фильтр batch, который группирует элементы массива (имя заимствовано из Twig 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!.

Ответ 9

An Angular2 версия решения @CodeExpress pure angular.

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>