Как разделить данные ng-repeat с тремя столбцами, используя бутстрап

Я использую ng-repeat с моим кодом. У меня есть "n" количество текстовых полей на основе ng-repeat. Я хочу выровнять текстовое поле с тремя столбцами.

это мой код

<div class="control-group" ng-repeat="oneExt in configAddr.ext">
    {{$index+1}}. 
    <input type="text" name="macAdr{{$index+1}}" 
           id="macAddress" ng-model="oneExt.newValue" value=""/>
</div>

Ответ 1

Самый надежный и технически правильный подход - преобразовать данные в контроллер. Вот простая функция и использование куска.

function chunk(arr, size) {
  var newArr = [];
  for (var i=0; i<arr.length; i+=size) {
    newArr.push(arr.slice(i, i+size));
  }
  return newArr;
}

$scope.chunkedData = chunk(myData, 3);

Тогда ваше представление будет выглядеть так:

<div class="row" ng-repeat="rows in chunkedData">
  <div class="span4" ng-repeat="item in rows">{{item}}</div>
</div>

Если у вас есть какие-либо входы в ng-repeat, вы, вероятно, захотите развязать/вернуться к массивам, поскольку данные будут изменены или отправлены. Здесь, как это будет выглядеть в $watch, так что данные всегда будут доступны в исходном объединенном формате:

$scope.$watch('chunkedData', function(val) {
  $scope.data = [].concat.apply([], val);
}, true); // deep watch

Многие люди предпочитают делать это в представлении с помощью фильтра. Это возможно, но его следует использовать только для показа! Если вы добавите входные данные в это отфильтрованное представление, это вызовет проблемы, которые может решить, но не очень или надежно.

Проблема с этим фильтром заключается в том, что он каждый раз возвращает новые вложенные массивы. Angular просматривает возвращаемое значение из фильтра. При первом запуске фильтра Angular знает значение, затем запускает его снова, чтобы гарантировать, что он будет изменен. Если оба значения одинаковы, цикл завершается. Если нет, фильтр будет срабатывать снова и снова, пока они не станут одинаковыми, или Angular реализует бесконечный цикл дайджеста, который происходит и завершается. Поскольку новые вложенные массивы/объекты ранее не отслеживались с помощью Angular, он всегда видит возвращаемое значение, отличное от предыдущего. Чтобы исправить эти "неустойчивые" фильтры, вы должны обернуть фильтр в функцию memoize. lodash имеет функцию memoize, а последняя версия lodash также включает функцию chunk, поэтому мы можем создать этот фильтр очень просто с помощью модулей npm и скомпилировать script с помощью browserify или webpack.

Помните: только дисплей! Фильтр в контроллере, если вы используете входы!

Установить lodash:

npm install lodash-node

Создайте фильтр:

var chunk = require('lodash-node/modern/array/chunk');
var memoize = require('lodash-node/modern/function/memoize');

angular.module('myModule', [])
.filter('chunk', function() {
  return memoize(chunk);
});

И вот образец с этим фильтром:

<div ng-repeat="row in ['a','b','c','d','e','f'] | chunk:3">
  <div class="column" ng-repeat="item in row">
    {{($parent.$index*row.length)+$index+1}}. {{item}}
  </div>
</div>

Элементы заказа вертикально

1  4
2  5
3  6

Что касается вертикальных столбцов (список сверху вниз), а не горизонтальный (слева направо), то конкретная реализация зависит от желаемой семантики. Списки, разделяющие неравномерно, могут быть распределены разными способами. Здесь один из способов:

<div ng-repeat="row in columns">
  <div class="column" ng-repeat="item in row">
    {{item}}
  </div>
</div>
var data = ['a','b','c','d','e','f','g'];
$scope.columns = columnize(data, 3);
function columnize(input, cols) {
  var arr = [];
  for(i = 0; i < input.length; i++) {
    var colIdx = i % cols;
    arr[colIdx] = arr[colIdx] || [];
    arr[colIdx].push(input[i]);
  }
  return arr;
}

Однако самый простой и просто простой способ получить столбцы - использовать столбцы CSS:

.columns {
  columns: 3;
}
<div class="columns">
  <div ng-repeat="item in ['a','b','c','d','e','f','g']">
    {{item}}
  </div>
</div>

Ответ 2

Это решение очень просто:

JSON:

[{id:"1",name:"testA"},{id:"2",name:"test B"},{id:"3",name:"test C"},{id:"4",name:"test D"},{id:"5",name:"test E"}]

HTML:

<div ng-controller="MyCtrl">
  <table>
    <tr ng-repeat="item in items" ng-switch on="$index % 3">
      <td ng-switch-when="0">
        {{items[$index].id}} {{items[$index].name}}
      </td>
      <td ng-switch-when="0">
        <span ng-show="items[$index+1]">
          {{items[$index+1].id}} {{items[$index+1].name}}
        </span>
      </td>
      <td ng-switch-when="0">
        <span ng-show="items[$index+2]">    
          {{items[$index+2].id}} {{items[$index+2].name}}
        </span>
      </td>
    </tr>
  </table>
</div>

ДЕМО в FIDDLE

enter image description here

Ответ 3

Чистое, адаптируемое решение, которое не требует обработки данных:

HTML:

<div class="control-group" class="label"
    ng-repeat="oneExt in configAddr.ext"
    ng-class="{'new-row': startNewRow($index, columnBreak) }">
    {{$index+1}}. 
    <input type="text" name="macAdr{{$index+1}}" 
       id="macAddress{{$index}}" ng-model="oneExt.newValue" />
</div>

CSS:

.label {
    float: left;
    text-align: left;
}
.new-row {
    clear: left;
}

JavaScript:

$scope.columnBreak = 3; //Max number of colunms
$scope.startNewRow = function (index, count) {
    return ((index) % count) === 0;
};

Это простое решение для динамического рендеринга данных в строки и столбцы, без необходимости манипулировать массивом данных, которые вы пытаетесь отобразить. Плюс, если вы попытаетесь изменить размер окна браузера, вы увидите, что сетка динамически адаптируется к размеру экрана /div.

(Я также добавил суффикс {{$ index}} к вашему id, так как ng-repeat попытается создать несколько элементов с тем же идентификатором, если вы этого не сделаете.)

Аналогичный рабочий пример

Ответ 4

Ответ m59 довольно хорош. Единственное, что мне не нравится в этом, это то, что он использует div для того, что потенциально может быть данными для таблицы.

Таким образом, в сочетании с фильтром m59 (ответьте где-то выше), вот как отобразить его в таблице.

<table>
    <tr class="" ng-repeat="rows in foos | chunk:2">
        <td ng-repeat="item in rows">{{item}}</td>
    </tr>
</table>

Ответ 5

Ниже приведен более простой способ:

<table>
    <tr ng-repeat="item in lists" ng-hide="$index%2!==0">
        <td>
            <label>{{ lists[$index].name}}</label>
        </td>
        <td ng-hide="!lists[$index+1]">
            <label>{{ lists[$index+1].name}}</label>
        </td>
    </tr>
</table>

Ответ Cumulo Nimbus полезен для меня, но я хочу, чтобы эта сетка была обернута div, которая может отображать полосу прокрутки, когда список слишком длинный.

Чтобы достичь этого, я добавил style="height:200px; overflow:auto" в div вокруг таблицы, который заставляет его отображаться как один столбец.

Теперь работает для длины массива.

Ответ 6

У меня есть функция и она хранится в службе, поэтому я могу использовать ее во всем моем приложении:

Услуги:

app.service('SplitArrayService', function () {
return {
    SplitArray: function (array, columns) {
        if (array.length <= columns) {
            return [array];
        };

        var rowsNum = Math.ceil(array.length / columns);

        var rowsArray = new Array(rowsNum);

        for (var i = 0; i < rowsNum; i++) {
            var columnsArray = new Array(columns);
            for (j = 0; j < columns; j++) {
                var index = i * columns + j;

                if (index < array.length) {
                    columnsArray[j] = array[index];
                } else {
                    break;
                }
            }
            rowsArray[i] = columnsArray;
        }
        return rowsArray;
    }
}

});

Контроллер:

$scope.rows   = SplitArrayService.SplitArray($scope.images, 3); //im splitting an array of images into 3 columns

Разметка:

 <div class="col-sm-12" ng-repeat="row in imageRows">
     <div class="col-sm-4" ng-repeat="image in row">
         <img class="img-responsive" ng-src="{{image.src}}">
     </div>
 </div>

Ответ 7

Мой подход был смешением вещей.

Моя цель состояла в том, чтобы адаптировать сетку к размеру экрана. Я хотел 3 столбца для lg, 2 столбца для sm и md и 1 столбец для xs.

Сначала я создал следующую функцию области видимости, используя службу angular $window:

$scope.findColNumberPerRow = function() {
    var nCols;
    var width = $window.innerWidth;

    if (width < 768) {
        // xs
        nCols = 1;
    }
    else if(width >= 768 && width < 1200) {
         // sm and md
         nCols = 2
    } else if (width >= 1200) {
        // lg
        nCols = 3;
    }
    return nCols;
};

Затем я использовал класс, предложенный @Cumulo Nimbus:

.new-row {
    clear: left;
}

В div, содержащем ng-repeat, я добавил директиву resizable, как описано в этой странице, так что каждое временное окно, angular $window служба обновляется новыми значениями.

В конечном счете, в повторном div я:

ng-repeat="user in users" ng-class="{'new-row': ($index % findColNumberPerRow() === 0) }"

Пожалуйста, дайте мне знать любые недостатки в этом подходе.

Надеюсь, это будет полезно.

Ответ 8

Простой трюк с CSS "clearfix", рекомендованный Bootstrap:

<div class="row">
      <div ng-repeat-start="value in values" class="col-md-4">
        {{value}}
      </div>
      <div ng-repeat-end ng-if="$index % 3 == 0" class="clearfix"></div>
</div>

Ответ 9

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

Во внутреннем столбце я повторяю строку, пока не будет достигнут предел. Предел определяется путем деления длины массива элементов на количество желаемых столбцов, в этом случае на два. Метод разделения находится на контроллере и передается текущая длина массива в качестве параметра. Функция slice (0, array.length/columnCount) JavaScript применила предел к повторителю.

Затем вызывается второй повторитель столбцов и повторяет фрагмент (array.length/columnCount, array.length), который создает вторую половину массива в столбце 2.

<div class="row" ng-repeat="GroupAccess in UsersController.SelectedUser.Access" ng-class="{even: $even, odd: $odd}">
    <div class="col-md-12 col-xs-12" style=" padding-left:15px;">
        <label style="margin-bottom:2px;"><input type="checkbox" ng-model="GroupAccess.isset" />{{GroupAccess.groupname}}</label>
    </div>
    <div class="row" style=" padding-left:15px;">
        <div class="col-md-1 col-xs-0"></div>
        <div class="col-md-3 col-xs-2">
            <div style="line-height:11px; margin-left:2px; margin-bottom:2px;" ng-repeat="Feature in GroupAccess.features.slice(0, state.DivideBy2(GroupAccess.features.length))">
                <span class="GrpFeature">{{Feature.featurename}}</span>
            </div>
        </div>
        <div class="col-md-3 col-xs-2">
            <div style="line-height:11px; margin-left:2px; margin-bottom:2px;" ng-repeat="Feature in GroupAccess.features.slice( state.DivideBy2(GroupAccess.features.length), GroupAccess.features.length )">
                <span class="GrpFeature">{{Feature.featurename}}</span>
            </div>
        </div>
        <div class="col-md-5 col-xs-8">
        </div>
    </div>
</div>


// called to format two columns
state.DivideBy2 = function(nValue) {
    return Math.ceil(nValue /2);
};

Надеюсь, что это поможет взглянуть на решение по-другому. (PS это мой первый пост здесь: -))

Ответ 10

Я исправляю без .row

<div class="col col-33 left" ng-repeat="photo in photos">
   Content here...
</div>

и css

.left {
  float: left;
}

Ответ 11

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

Ответ 12

Здесь легко-хакерский способ сделать это. Он более ручной и заканчивается грязной разметкой. Я не рекомендую это, но бывают ситуации, когда это может быть полезно.

Вот ссылка http://jsfiddle.net/m0nk3y/9wcbpydq/

HTML:

<div ng-controller="myController">
    <div class="row">
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3)">
                {{ person.name }}
                </div>
            </div>
        </div>
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3):Math.ceil(data.length/3)">
                {{ person.name }}
                </div>
            </div>
        </div>
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3):Math.ceil(data.length/3)*2">
                {{ person.name }}
                </div>
            </div>
        </div>
    </div>
</div>

JS:

var app = angular.module('myApp', []);

app.controller('myController', function($scope) {

    $scope.Math = Math;
    $scope.data = [
        {"name":"Person A"},
        ...
    ];
});

Эта настройка требует от нас использования некоторой Math в разметке:/, поэтому вам нужно будет ввести Math, добавив эту строку: $scope.Math = Math;

Ответ 13

Все эти ответы кажутся массово переработанными.

Напротив, самым простым способом было бы создать входные divs в бутстрапе col-md-4, затем bootstrap автоматически отформатирует его на 3 столбца из-за 12-ти столбцов бутстрапа:

<div class="col-md-12">
    <div class="control-group" ng-repeat="oneExt in configAddr.ext">
        <div class="col-md-4">
            <input type="text" name="macAdr{{$index}}"
                   id="macAddress" ng-model="oneExt.newValue" value="" />
        </div>
    </div>
</div>

Ответ 14

Избавление от m59 очень хорошее. Я обнаружил, что входы модели будут размыты, если они изменились, поэтому вы могли менять только один символ за раз. Это новый для списков объектов для тех, кто ему нужен:

EDIT Обновлено для обработки нескольких фильтров на одной странице

app.filter('partition', function() {
  var cache = {}; // holds old arrays for difference repeat scopes
  var filter = function(newArr, size, scope) {
    var i,
      oldLength = 0,
      newLength = 0,
      arr = [],
      id = scope.$id,
      currentArr = cache[id];
    if (!newArr) return;

    if (currentArr) {
      for (i = 0; i < currentArr.length; i++) {
        oldLength += currentArr[i].length;
      }
    }
    if (newArr.length == oldLength) {
      return currentArr; // so we keep the old object and prevent rebuild (it blurs inputs)
    } else {
      for (i = 0; i < newArr.length; i += size) {
        arr.push(newArr.slice(i, i + size));
      }
      cache[id] = arr;
      return arr;
    }
  };
  return filter;
}); 

И это будет использование:

<div ng-repeat="row in items | partition:3:this">
  <span class="span4">
    {{ $index }}
  </span>
</div>

Ответ 15

Я новичок в bootstrap и angularjs, но это также может сделать Array на 4 элемента как одну группу, результат будет почти как 3 столбца. Этот трюк использует принцип прерывания бутстрепа.

<div class="row">
 <div class="col-sm-4" data-ng-repeat="item in items">
  <div class="some-special-class">
   {{item.XX}}
  </div>
 </div>
</div>

Ответ 16

Другой способ: установить width:33.33%; float:left в обертку div следующим образом:

<div ng-repeat="right in rights" style="width: 33.33%;float: left;">
    <span style="width:60px;display:inline-block;text-align:right">{{$index}}</span>
    <input type="number" style="width:50px;display:inline-block" ">
</div>

Ответ 17

Lodash теперь имеет встроенный метод chunk, который я лично использую: https://lodash.com/docs#chunk

Исходя из этого, код контроллера может выглядеть следующим образом:

$scope.groupedUsers = _.chunk( $scope.users, 3 )

Просмотр кода:

<div class="row" ng-repeat="rows in groupedUsers">
  <div class="span4" ng-repeat="item in rows">{{item}}</div>
</div>

Ответ 18

Я оказался в подобном случае, желая создать группы отображения по 3 столбца каждый. Однако, хотя я использовал загрузку, я пытался разделить эти группы на разные родительские div. Я также хотел сделать что-то общее полезное.

Я подошел к нему с помощью 2 ng-repeat, как показано ниже:

<div ng-repeat="items in quotes" ng-if="!($index % 3)">
  <div ng-repeat="quote in quotes" ng-if="$index <= $parent.$index + 2 && $index >= $parent.$index">
                ... some content ...
  </div>
</div>

Это позволяет легко изменить на другое количество столбцов и разделить на несколько родительских div.

Ответ 19

На всякий случай, если кто-то хочет Angular 7 (и более позднюю версию), вот пример, который я использовал в одном из своих приложений:

HTML:

                   <div class="container-fluid">
                    <div class="row" *ngFor="let reports of chunkData; index as i">
                        <div *ngFor="let report of reports" class="col-4 col-sm-4"
                            style="border-style:solid;background-color: antiquewhite">{{report}}</div>
                    </div>
                </div>

Ответ 20

это отвечает на исходный вопрос, который заключается в том, как получить 1,2,3 в столбце. - спросил kuppu 8 февраля 14 в 13:47

angularjs code:

function GetStaffForFloor(floor) {
    var promiseGet = Directory_Service.getAllStaff(floor);
    promiseGet.then(function (pl) {
        $scope.staffList = chunk(pl.data, 3); //pl.data; //
    },
    function (errorOD) {
        $log.error('Errored while getting staff list.', errorOD);
    });
}
function chunk(array, columns) {
    var numberOfRows = Math.ceil(array.length / columns);

    //puts 1, 2, 3 into column
    var newRow = []; //array is row-based.
    for (var i = 0; i < array.length; i++) {
        var columnData = new Array(columns);
        if (i == numberOfRows) break;
        for (j = 0; j < columns; j++)
        {
            columnData[j] = array[i + numberOfRows * j];
        }
        newRow.push(columnData); 
    }
    return newRow;

    ////this works but 1, 2, 3 is in row
    //var newRow = [];
    //for (var i = 0; i < array.length; i += columns) {
    //    newRow.push(array.slice(i, i + columns)); //push effectively does the pivot. array is row-based.
    //}
    //return newRow;
};

Просмотреть код (примечание: использование бутстрапа 3):

<div class="staffContainer">
    <div class="row" ng-repeat="staff in staffList">
        <div class="col-md-4" ng-repeat="item in staff">{{item.FullName.length > 0 ? item.FullName + ": Rm " + item.RoomNumber : ""}}</div>
    </div>
</div>

Ответ 21

Этот код поможет выровнять элементы с тремя столбцами в режиме lg и md, двумя столбцами в режиме sm и одним столбцом в режиме xs.

<div class="row">
<div ng-repeat="oneExt in configAddr.ext">
    <div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
        {$index+1}}. 
        <input type="text" name="macAdr{{$index+1}}" 
       id="macAddress" ng-model="oneExt.newValue" value=""/>
    </div>
</div>