Angular ng-repeat добавляет строку начальной загрузки каждые 3 или 4 столбца


111

Я ищу правильный шаблон для вставки класса строки начальной загрузки каждые 3 столбца. Мне это нужно, потому что cols не имеет фиксированной высоты (и я не хочу ее исправлять), поэтому это нарушает мой дизайн!

Вот мой код:

<div ng-repeat="product in products">
    <div ng-if="$index % 3 == 0" class="row">
        <div class="col-sm-4" >
            ...
        </div>
    </div>
</div>

Но в каждой строке отображается только один продукт. В качестве конечного результата я хочу:

<div class="row">
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
</div>
<div class="row">
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
</div>

Могу ли я добиться этого с помощью только шаблона ng-repeat (без директивы или контроллера)? В документации представлены ng-repeat-start и ng-repeat-end, но я не могу понять, как это использовать, именно в этом случае использования! Я чувствую, что это то, что мы часто используем при создании шаблонов начальной загрузки! ? Спасибо


Я думаю, вы должны моделировать свои данные таким образом, чтобы соответствовать вашему дизайну, вероятно, это должен быть многомерный массив или объект с представлением строк и столбцов, тогда вы должны перебирать строки и использовать директиву условного класса «ng-class» и внутри строки затем вы должны перебирать столбцы.
— antanas_sepikas

Интересное и, безусловно, рабочее решение, но в тот день, когда я хочу отображать 4 продукта в строке вместо 3, у меня есть, чтобы изменить свою структуру данных, я бы предпочел, чтобы это оставалось в «области» чистой функциональности отображения ...
— hugsbrugs

Я вижу, тогда вам, вероятно, следует выполнять итерацию по частям, как в ответе Ариэля, также вы можете найти этот пост stackoverflow.com/questions/18564888/… полезным.
— antanas_sepikas

Думаю, это именно то, что вы ищете: stackoverflow.com/a/30426750/1943442
— user1943442

Ответы:


164

Ответ, получивший наибольшее количество голосов, хотя и эффективен, это не то, что я считаю подходом angular, и при этом он не использует собственные классы начальной загрузки, которые предназначены для решения этой ситуации. Как упоминалось в @claies, .clearfixкласс предназначен для таких ситуаций. На мой взгляд, самая чистая реализация выглядит следующим образом:

<div class="row">
    <div ng-repeat="product in products">
        <div class="clearfix" ng-if="$index % 3 == 0"></div>
        <div class="col-sm-4">
            <h2>{{product.title}}</h2>
        </div>
    </div>
</div>

Эта структура позволяет избежать беспорядочной индексации массива продуктов, позволяет использовать чистую точечную нотацию и использовать класс clearfix по прямому назначению.


6
Это хорошая идея, однако, если вы заинтересованы в использовании flexbox, вы должны использовать это в строке, а не в div внутри строк, чтобы позволить каждому box / div иметь одинаковую высоту. Clearfix хорош, но не помогает поддерживать все в порядке.
— Coded Container

3
Это мой предпочтительный ответ. Очень чисто и просто.
— Hinrich

1
Отлично работает и для моей реализации! :)
— Уилл Штрол

1
Отлично ... Этот ответ следует признать принятым!
— Velu

3
Это идеальный ответ
— Deepu

148

Я знаю, что уже немного поздно, но все же может кому-то помочь. У меня так получилось:

<div ng-repeat="product in products" ng-if="$index % 3 == 0" class="row">
    <div class="col-xs-4">{{products[$index]}}</div>
    <div class="col-xs-4" ng-if="products.length > ($index + 1)">{{products[$index + 1]}}</div>
    <div class="col-xs-4" ng-if="products.length > ($index + 2)">{{products[$index + 2]}}</div>
</div>

jsfiddle


3
это действительно мне очень помогло !! Спасибо.
— SupimpaAllTheWay

Это действительно легко реализовать! Спасибо!
— Manas Bajaj,

Мне очень помогли. Спасибо!
— Ахмад Аджми

17
Отличное решение, однако оно не проверяет, выходят ли $ index + 1 и $ index +2 за границы массива. Последние два дивизиона требуют ng-if="$index+1 < products.length"иng-if="$index+2 < products.length"
— Мустафа Озтюрк

Можем ли мы сделать это для пары ключ-значение. получить следующий ключ? Вместо получения $ index + 1
— bpbhat77

25

Хорошо, это решение намного проще, чем уже здесь, и допускает разную ширину столбцов для разной ширины устройства.

<div class="row">
    <div ng-repeat="image in images">
        <div class="col-xs-6 col-sm-4 col-md-3 col-lg-2">
            ... your content here ...
        </div>
        <div class="clearfix visible-lg" ng-if="($index + 1) % 6 == 0"></div>
        <div class="clearfix visible-md" ng-if="($index + 1) % 4 == 0"></div>
        <div class="clearfix visible-sm" ng-if="($index + 1) % 3 == 0"></div>
        <div class="clearfix visible-xs" ng-if="($index + 1) % 2 == 0"></div>
    </div>
</div>

Обратите внимание, что % 6часть должна равняться количеству результирующих столбцов. Итак, если в элементе столбца у вас есть класс, col-lg-2будет 6 столбцов, поэтому используйте ... % 6.

Этот метод (за исключением ng-if) фактически задокументирован здесь: Bootstrap docs


1
На мой взгляд, это лучшее решение.
— user216661

Если вы новичок в начальной загрузке, легко упустить из виду тот факт, что определение строк не требуется. Это сработало отлично и представляет собой чуть более полную версию решения Дункана.
— phosplait

Это именно то, что я искал.
— Hinrich

@phosplait В чем преимущество этого перед Дунканом?
— Jeppe

17

Хотя то, чего вы хотите достичь, может быть полезно, есть еще один вариант, который, я думаю, вы могли упустить из виду, и он намного проще.

Вы правы, таблицы Bootstrap ведут себя странно, когда у вас есть столбцы с нестандартной высотой. Однако существует класс начальной загрузки, созданный для борьбы с этой проблемой и выполнения адаптивного сброса .

просто создайте пустое <div class="clearfix"></div>поле перед началом каждой новой строки, чтобы позволить поплавкам сбрасываться, а столбцы возвращаться в свои правильные положения.

вот бутплей .


Это не устраняет отрицательные 15 пикселей поля, которые каждый .row имеет для начальной загрузки.
— Logus

Можно ли flexсделать столбцы одинаковой высоты?
— Алиссон,

16

Спасибо за ваши предложения, вы меня на правильном пути!

Давайте рассмотрим полное объяснение:

  • По умолчанию HTTP-запрос AngularJS возвращает объект

  • Итак, если вы хотите использовать функцию @Ariel Array.prototype.chunk, вам нужно сначала преобразовать объект в массив.

  • А затем использовать функцию chunk В ВАШЕМ КОНТРОЛЛЕРЕ, иначе, если она используется непосредственно в ng-repeat, это приведет вас к ошибке infdig . Итоговый контроллер выглядит:

    // Initialize products to empty list
    $scope.products = [];
    
    // Load products from config file
    $resource("/json/shoppinglist.json").get(function (data_object)
    {
        // Transform object into array
        var data_array =[];
        for( var i in data_object ) {
            if (typeof data_object[i] === 'object' && data_object[i].hasOwnProperty("name")){
                data_array.push(data_object[i]);
            }
        }
        // Chunk Array and apply scope
        $scope.products = data_array.chunk(3);
    });

И HTML становится:

<div class="row" ng-repeat="productrow in products">

    <div class="col-sm-4" ng-repeat="product in productrow">

С другой стороны, я решил напрямую вернуть массив [] вместо объекта {} из моего файла JSON. Таким образом, контроллер становится (обратите внимание на специальный синтаксис isArray: true ):

    // Initialize products to empty list 
    $scope.products = [];

    // Load products from config file
    $resource("/json/shoppinglist.json").query({method:'GET', isArray:true}, function (data_array)
    {
        $scope.products = data_array.chunk(3);
    });

HTML останется таким же, как указано выше.

ОПТИМИЗАЦИЯ

Последний вопрос в ожидании: как сделать его 100% AngularJS без расширения массива javascript с помощью функции chunk ... если некоторые люди хотят показать нам, подходят ли ng-repeat-start и ng-repeat-end .. . Мне любопытно ;)

РЕШЕНИЕ АНДРЕЯ

Благодаря @Andrew мы теперь знаем, что добавление класса очистки начальной загрузки каждые три (или любое другое число) элементов исправляет проблему отображения из-за разной высоты блока.

Итак, HTML становится:

<div class="row">

    <div ng-repeat="product in products">

        <div ng-if="$index % 3 == 0" class="clearfix"></div>

        <div class="col-sm-4"> My product descrition with {{product.property}}

И ваш контроллер остается довольно мягким с удаленной функцией chunck :

// Initialize products to empty list 
        $scope.products = [];

        // Load products from config file
        $resource("/json/shoppinglist.json").query({method:'GET', isArray:true}, function (data_array)
        {
            //$scope.products = data_array.chunk(3);
            $scope.products = data_array;
        });

7

Вы можете сделать это без директивы, но я не уверен, что это лучший способ. Для этого вы должны создать массив массива из данных, которые вы хотите отобразить в таблице, и после этого использовать 2 ng-repeat для итерации по массиву.

чтобы создать массив для отображения, используйте эту функцию, например, products.chunk (3)

Array.prototype.chunk = function(chunkSize) {
    var array=this;
    return [].concat.apply([],
        array.map(function(elem,i) {
            return i%chunkSize ? [] : [array.slice(i,i+chunkSize)];
        })
    );
}

а затем сделайте что-то подобное, используя 2 ng-repeat

<div class="row" ng-repeat="row in products.chunk(3)">
  <div class="col-sm4" ng-repeat="item in row">
    {{item}}
  </div>
</div>

7

На основе решения Alpar с использованием только шаблонов с anidated ng-repeat. Работает как с полными, так и с частично пустыми строками:

<div data-ng-app="" data-ng-init="products='soda','beer','water','milk','wine']" class="container">
    <div ng-repeat="product in products" ng-if="$index % 3 == 0" class="row">
        <div class="col-xs-4" 
            ng-repeat="product in products.slice($index, ($index+3 > products.length ? 
            products.length : $index+3))"> {{product}}</div>
    </div>
</div>

JSFiddle


5

Я только что решил, что работает только в шаблоне. Решение

    <span ng-repeat="gettingParentIndex in products">
        <div class="row" ng-if="$index<products.length/2+1">    <!-- 2 columns -->
            <span ng-repeat="product in products">
                <div class="col-sm-6" ng-if="$index>=2*$parent.$index && $index <= 2*($parent.$index+1)-1"> <!-- 2 columns -->
                    {{product.foo}}
                </div>
            </span>
        </div>
    </span>

Point использует данные дважды, один - для внешнего цикла. Дополнительные теги диапазона останутся, но это зависит от того, как вы торгуете.

Если это трехколоночный макет, он будет похож на

    <span ng-repeat="gettingParentIndex in products">
        <div class="row" ng-if="$index<products.length/3+1">    <!-- 3 columns -->
            <span ng-repeat="product in products">
                <div class="col-sm-4" ng-if="$index>=3*$parent.$index && $index <= 3*($parent.$index+1)-1"> <!-- 3 columns -->
                    {{product.foo}}
                </div>
            </span>
        </div>
    </span>

Честно хотел

$index<Math.ceil(products.length/3)

Хотя не вышло.


Я пробовал это решение, чтобы реализовать по 2 элемента в каждой строке. Например, у меня есть 5 элементов в списке, поэтому у меня должен быть вывод: 3 строки с 2 элементами / столбцами в первых 2 строках и 1 столбцом в последней строке. Проблема в том, что я получаю здесь 5 строк, а последние 2 строки пусты. Хотите знать, как это исправить? Спасибо
— Maverick Riz

@MaverickAzy, спасибо за попытку. Я знаю, что существует проблема, заключающаяся в том, что если высота этих элементов отличается, это не работает.
— Ватару 06

Высота элементов практически одинакова. Проблема в том, что я должен получить только 3 строки, но получить 5 строк с двумя последними пустыми строками. Вы можете мне сказать, если products.length равно 5, то 5/2 + 1 =? Эта логика мне не понятна в строке № 2 для строки класса.
— Maverick Riz

@MaverickAzy эти пустые строки должны быть созданы как есть. Это портит ваш макет?
— Ватару 06

нет, это не портит макет. Единственное беспокойство вызывает пустые строки. Очень признателен, если вы можете мне с этим помочь. Спасибо
— Maverick Riz

5

Еще одно небольшое улучшение ответа @Duncan и других ответов на основе элемента clearfix. Если вы хотите сделать контент интерактивным, вам понадобится z-index> 0, иначе clearfix будет перекрывать контент и обрабатывать щелчок.

Этот пример не работает (вы не видите указатель курсора, и щелчок ничего не сделает):

<div class="row">
    <div ng-repeat="product in products">
        <div class="clearfix" ng-if="$index % 3 == 0"></div>
        <div class="col-sm-4" style="cursor: pointer" ng-click="doSomething()">
            <h2>{{product.title}}</h2>
        </div>
    </div>
</div>

Пока это фиксированный :

<div class="row">
    <div ng-repeat-start="product in products" class="clearfix" ng-if="$index % 3 == 0"></div>
    <div ng-repeat-end class="col-sm-4" style="cursor: pointer; z-index: 1" ng-click="doSomething()">
            <h2>{{product.title}}</h2>
    </div>
</div>

Я добавил, z-index: 1чтобы контент увеличивался по сравнению с clearfix, и я удалил контейнер div, используя вместо него ng-repeat-startи ng-repeat-end(доступно в AngularJS 1.2), потому что он не работал z-index.

Надеюсь это поможет!

Обновить

Plunker: http://plnkr.co/edit/4w5wZj


Работает ли это с flexстроками, чтобы столбцы были одинаковой высоты?
— Алиссон,

Я не уверен, что понимаю ваш вопрос. Это быстрый инструмент, показывающий, что делает этот код: plnkr.co/edit/4w5wZj?p=preview . На словах clearfix правильно выравнивает вторую строку заголовков: все они начинаются с одной и той же точки, но у них по-прежнему разная высота (как вы можете видеть благодаря цвету фона). Попробуйте удалить класс clearfix, чтобы узнать, каково поведение по умолчанию. Я использовал flexbox только один или два раза, но у него много свойств css, и я уверен, что вы сможете найти то, что ищете.
— McGiogen

bootstrap предоставляет пример того, как сделать все столбцы в одной строке, чтобы получить одинаковую высоту самого высокого столбца. Я должен был это использовать. Проблема в том, что он теряет возможность переноса на новую строку, когда имеется более 12 столбцов, поэтому вам нужно вручную создавать новые строки. Изучив немного больше, я мог бы получить решение и опубликовать его здесь в качестве ответа, хотя я не знаю, лучший ли он. В любом случае, спасибо, ваш ответ мне помог!
— Алиссон

Я впервые вижу этот пример, он действительно полезен! Рад помочь вам.
— McGiogen

4

я решил это с помощью ng-class

<div ng-repeat="item in items">
    <div ng-class="{ 'row': ($index + 1) % 4 == 0 }">
        <div class="col-md-3">
            {{item.name}}
        </div>
    </div>
</div>

2

Лучший способ применить класс - использовать ng-class. Его можно использовать для применения классов на основе некоторого условия.

<div ng-repeat="product in products">
   <div ng-class="getRowClass($index)">
       <div class="col-sm-4" >
           <!-- your code -->
       </div>
   </div>

а затем в вашем контроллере

$scope.getRowClass = function(index){
    if(index%3 == 0){
     return "row";
    }
}

2

После объединения многих ответов и предложений здесь это мой окончательный ответ, который хорошо работает flex, что позволяет нам создавать столбцы с одинаковой высотой, он также проверяет последний индекс, и вам не нужно повторять внутренний HTML. Не использует clearfix:

<div ng-repeat="prod in productsFiltered=(products | filter:myInputFilter)" ng-if="$index % 3 == 0" class="row row-eq-height">
    <div ng-repeat="i in [0, 1, 2]" ng-init="product = productsFiltered[$parent.$parent.$index + i]"  ng-if="$parent.$index + i < productsFiltered.length" class="col-xs-4">
        <div class="col-xs-12">{{ product.name }}</div>
    </div>
</div>

Результат будет примерно таким:

<div class="row row-eq-height">
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
</div>
<div class="row row-eq-height">
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
</div>

1

Небольшая модификация в решении @alpar

<div data-ng-app="" data-ng-init="products=['A','B','C','D','E','F', 'G','H','I','J','K','L']" class="container">
    <div ng-repeat="product in products" ng-if="$index % 6 == 0" class="row">
        <div class="col-xs-2" ng-repeat="idx in [0,1,2,3,4,5]">
        {{products[idx+$parent.$index]}} <!-- When this HTML is Big it's useful approach -->
        </div>
    </div>
</div>

jsfiddle


0

Это сработало для меня, без сращивания или чего-то еще:

HTML

<div class="row" ng-repeat="row in rows() track by $index">
    <div class="col-md-3" ng-repeat="item in items" ng-if="indexInRange($index,$parent.$index)"></div>
</div>

JavaScript

var columnsPerRow = 4;
$scope.rows = function() {
  return new Array(columnsPerRow);
};
$scope.indexInRange = function(columnIndex,rowIndex) {
  return columnIndex >= (rowIndex * columnsPerRow) && columnIndex < (rowIndex * columnsPerRow) + columnsPerRow;
};

0

Born Solutions - лучший вариант, просто нужно немного подправить, чтобы удовлетворить потребности, у меня были разные адаптивные решения, и я немного изменился

<div ng-repeat="post in posts">
    <div class="vechicle-single col-lg-4 col-md-6 col-sm-12 col-xs-12">
    </div>
    <div class="clearfix visible-lg" ng-if="($index + 1) % 3 == 0"></div>
    <div class="clearfix visible-md" ng-if="($index + 1) % 2 == 0"></div>
    <div class="clearfix visible-sm" ng-if="($index + 1) % 1 == 0"></div>
    <div class="clearfix visible-xs" ng-if="($index + 1) % 1 == 0"></div>
</div>

0

Основываясь на ответе Alpar, вот более общий способ разбить один список элементов на несколько контейнеров (строки, столбцы, корзины и т. Д.):

<div class="row" ng-repeat="row in [0,1,2]">
  <div class="col" ng-repeat="item in $ctrl.items" ng-if="$index % 3 == row">
    <span>{{item.name}}</span>
  </div>
</div> 

для списка из 10 элементов генерирует:

<div class="row">
  <div class="col"><span>Item 1</span></div>
  <div class="col"><span>Item 4</span></div>
  <div class="col"><span>Item 7</span></div>
  <div class="col"><span>Item 10</span></div>
</div> 
<div class="row">
  <div class="col"><span>Item 2</span></div>
  <div class="col"><span>Item 5</span></div>
  <div class="col"><span>Item 8</span></div>
</div> 
<div class="row">
  <div class="col"><span>Item 3</span></div>
  <div class="col"><span>Item 6</span></div>
  <div class="col"><span>Item 9</span></div>
</div> 

Количество контейнеров можно быстро закодировать в функцию контроллера:

JS (ES6)

$scope.rowList = function(rows) {
  return Array(rows).fill().map((x,i)=>i);
}
$scope.rows = 2;

HTML

<div class="row" ng-repeat="row in rowList(rows)">
  <div ng-repeat="item in $ctrl.items" ng-if="$index % rows == row">
    ...

Такой подход позволяет избежать дублирования разметки элемента ( <span>{{item.name}}</span>в данном случае) в исходном шаблоне - не очень большая победа для простого диапазона, но для более сложной структуры DOM (которая у меня была) это помогает сохранить шаблон DRY.


0

Обновление 2019 - Bootstrap 4

Поскольку в Bootstrap 3 используются числа с плавающей запятой, для предотвращения неравномерного переноса столбцов требуется, чтобы clearfix сбрасывал каждые n (3 или 4) columns ( .col-*) .row.

Теперь, когда Bootstrap 4 использует flexbox , больше нет необходимости переносить столбцы в отдельные .rowтеги или вставлять дополнительные div, чтобы заставить cols переноситься каждые n столбцов.

Вы можете просто повторить все столбцы в одном .rowконтейнере.

Например, 3 столбца в каждой визуальной строке:

<div class="row">
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     (...repeat for number of items)
</div>

Итак, для Bootstrap ng-repeat просто:

  <div class="row">
      <div class="col-4" ng-repeat="item in items">
          ... {{ item }}
      </div>
  </div>

Демо: https://www.codeply.com/go/Z3IjLRsJXX


0

Я сделал это только с помощью boostrap, вы должны быть очень осторожны в расположении строки и столбца, вот мой пример.

<section>
<div class="container">
        <div ng-app="myApp">
        
                <div ng-controller="SubregionController">
                    <div class="row text-center">
                        <div class="col-md-4" ng-repeat="post in posts">
                            <div >
                                <div>{{post.title}}</div>
                            </div>
                        </div>
                    
                    </div>
                </div>        
        </div>
    </div>
</div> 

</section>

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.