как разделить данные ng-repeat на три столбца с помощью bootstrap

Я использую 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>

19 ответов


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

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, он всегда видит возвращаемое значение отличным от предыдущего. Чтобы исправить эти "нестабильные" фильтры, вы должны обернуть фильтр в


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

в формате 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>

демо в скрипке

enter image description here


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

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

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

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

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


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

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

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

следующий более простой способ:

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

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


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

сервис:

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>

Мой подход был смешанным.

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

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

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

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

надеюсь, это может быть полезно.


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

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

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

<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 Это мой первый пост здесь! : -))


Я могу исправить без .row

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

и css

.left {
  float: left;
}

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

вот скрипка ссылка 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"},
        ...
    ];
});

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


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

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

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

простой трюк с 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>

много преимуществ: эффективный, быстрый, используя рекомендации Boostrap, избегая возможных проблем $digest, и не изменяя угловую модель.


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

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

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>

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

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

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>

другой способ-установить 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>

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

Я подошел к нему с 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>

это делает его очень легким изменить к различному числу столбцов, и отделенный вне в несколько родительских divs.


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

код angularjs:

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

Просмотр кода (примечание: с помощью bootstrap 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>