web-dev-qa-db-ja.com

ブートストラップを使用してng-repeatデータを3列に分割する方法

私は自分のコードでng-repeatを使っています。私はng-repeatを基にした 'n'個のテキストボックスを持っています。テキストボックスを3列に並べたい。

これは私のコードです

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

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

これは、表示しようとしているデータの配列を操作する必要なしに、動的にデータを行と列にレンダリングするための簡単な方法です。さらに、ブラウザウィンドウのサイズを変更しようとすると、グリッドが画面/ divのサイズに動的に適応することがわかります。

(そうしないとng-repeatが同じIDを持つ複数の要素を作成しようとするので、私はあなたのIDに{{$ index}}接尾辞も追加しました。)

同様の実用的な例

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でこのグリッドをラップしたいです。

これを実現するために、テーブルの周りのdivにstyle="height:200px; overflow:auto"を追加しました。これにより、1列として表示されます。

今は1の配列の長さで動作します。

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

Bootstrapが推奨する "clearfix" CSSの簡単なトリック:

<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の推奨事項を使用し、$ダイジェストの問題を回避し、Angularモデルを変更しない。

2
Lastnico

私のアプローチは物事が混在していました。

私の目的は、画面サイズに合わせてグリッドを調整することでした。 lgに3列、smmdに2列、そしてxsに1列が必要でした。

まず、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;
}

ng-repeatを含むdivに、 このページ で説明されているようにresizableディレクティブを追加したので、ウィンドウのサイズが変更されるたびに、角度$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

この例では、外側のデータに2列にリストしたい内側の配列が含まれる入れ子になったリピーターを作成します。この概念は、3つ以上の列にも当てはまります。

内側の列で、制限に達するまで "行"を繰り返します。制限は、項目の配列の長さを必要な列数、この場合は2で割ることによって決定されます。除算方法はコントローラにあり、現在の配列の長さがパラメータとして渡されます。次に、JavaScriptのslice(0、array.length/columnCount)関数がリピーターに制限を適用しました。

次に2列目のリピーターが呼び出され、2列目に配列の後半部分を生成するslice(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

これらのすべての答えは、過度に設計されているように見えます。

最も簡単な方法は、col-md-4列のブートストラップに入力divを設定することです。ブートストラップは12列の性質を持つため、ブートストラップは自動的に3列にフォーマットします。

<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の非常に良い答えを基にしてください。モデル入力が変更されるとモデル入力がぼやけてしまうので、一度に変更できるのは1文字だけです。これはそれを必要とする人のためのオブジェクトのリストのための新しいものです:

EDIT 1ページに複数のフィルタを処理するように更新されました

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列の表示グループを生成したい。しかし、私はブートストラップを使用していましたが、これらのグループを異なる親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に分割することが非常に簡単になります。

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

私はブートストラップとangularjsで新しいですが、これは1つのグループとして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