it-swarm.com.ru

как разделить данные 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>
118
kuppu

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

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 и компилируя скрипт с помощью 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 {
  columns: 3;
}
<div class="columns">
  <div ng-repeat="item in ['a','b','c','d','e','f','g']">
    {{item}}
  </div>
</div>
251
m59

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

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

62
sgrillon

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

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

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

19
Cumulo Nimbus

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

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

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

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

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

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

7
geting

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

Обслуживание:

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

Простой трюк с 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 и не изменяя угловую модель.

2
Lastnico

Мой подход был смесью вещей.

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

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

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

2
igorauad

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

Вот ссылка на скрипку 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;

1
Gene Parcellano

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

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

Второй ретранслятор столбца затем вызывается и повторяет слайс (array.length/columnCount, array.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 это мой первый пост здесь! :-))

1
Harvey Mushman

Все эти ответы кажутся чрезмерно сложными. 

Безусловно, самым простым способом было бы установить входные элементы div в начальной загрузке столбца col-md-4, тогда загрузчик автоматически отформатирует его в 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>
1
cullimorer

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

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

и CSS

.left {
  float: left;
}
1
selahattinunlu
<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>
1
mrded

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

Опираясь на очень хороший ответ 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>
0
JSous

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

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

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

0
Matt Kindy

В Lodash теперь есть встроенный метод чанка, который я лично использую: 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>
0
sean2078

Я новичок в 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>
0
Nathan