HTML要素なしでng-repeatを使用する方法


142

ng-repeat(AngularJSで)を使用して、配列内のすべての要素をリストする必要があります。

複雑なのは、配列の各要素がテーブルの1行、2行、または3行に変換されることです。

場合、私は、有効なHTMLを作成することはできませんng-repeat要素で使用されて繰り返し要素のないタイプが間で許可されていないとして、<tbody>そして<tr>

たとえば、でng-repeatを使用すると<span>、次のようになります。

<table>
  <tbody>
    <span>
      <tr>...</tr>
    </span>
    <span>
      <tr>...</tr>
      <tr>...</tr>
      <tr>...</tr>
    </span>
    <span>
      <tr>...</tr>
      <tr>...</tr>
    </span>
  </tbody>
</table>          

これは無効なhtmlです。

しかし、私が生成する必要があるのは:

<table>
  <tbody>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
  </tbody>
</table>          

最初の行は最初の配列要素によって生成され、次の3行は2番目の配列要素によって生成され、5番目と6番目の行は最後の配列要素によって生成されています。

ng-repeatがバインドされているhtml要素がレンダリング中に「消える」ようにng-repeatを使用するにはどうすればよいですか?

それとも別の解決策がありますか?


明確化:生成された構造は以下のようになります。各配列要素は、テーブルの1〜3行を生成できます。答えは、理想的には配列要素ごとに0-n行をサポートする必要があります。

<table>
  <tbody>
    <!-- array element 0 -->
    <tr>
      <td>One row item</td>
    </tr>
    <!-- array element 1 -->
    <tr>
      <td>Three row item</td>
    </tr>
    <tr>
      <td>Some product details</td>
    </tr>
    <tr>
      <td>Customer ratings</td>
    </tr>
    <!-- array element 2 -->
    <tr>
      <td>Two row item</td>
    </tr>
    <tr>
      <td>Full description</td>
    </tr>
  </tbody>
</table>          

多分あなたは「replace:true」を使うべきですか?:この参照stackoverflow.com/questions/11426114/...

また、tr自体にng-repeatを使用できないのはなぜですか?

2
@Tommy、「配列の各要素は、テーブルの1行、2行、または3行のいずれかに変換されるため」。私がng-repeatを使用した場合、tr私が理解している限り、配列要素ごとに1行を取得します。
fadedbee

1
なるほど、分かりました。リピーターで使用する前に、モデルをフラット化することはできませんか?

@トミー、いいえ。tr1つの配列要素によって生成される1〜3 の構造は同じではありません。
fadedbee

回答:


66

更新: Angular 1.2以降を使用している場合は、ng-repeat-startを使用してください。@jmagnussonの回答を参照してください。

そうでなければ、ng-repeatをtbodyに置くのはどうですか?(AFAIK、単一のテーブルに複数の<tbody>があっても問題ありません。)

<tbody ng-repeat="row in array">
  <tr ng-repeat="item in row">
     <td>{{item}}</td>
  </tr>
</tbody>

62
これは技術的には機能するかもしれませんが、この一般的な使用例の答えは、任意の(そうでなければ不要な)マークアップを挿入する必要があるということです。同じ問題があります(行の繰り返しグループ-1つのヘッダーTRと1つ以上の子TRがグループとして繰り返されます)。他のテンプレートエンジンではささいなことですが、Angularのせいでハッキーです。
Brian Moeskau 2013年

1
同意した。DT + DD要素で繰り返しを実行しようとしています。無効な折り返し要素を追加せずにそれを行う方法はありません
David Lin

2
@ DavidLin、Angular v1.2(それが出てくるときはいつでも)、dtやddなどの複数の要素で繰り返すことができます:youtube.com/watch
v=W13qDdJDHp8&t=17m28s

これは、コンテナなしの制御フロー構文を使用してKnockoutJSでエレガントに処理されます:knockoutjs.com/documentation/foreach-binding.html。Angularがすぐに似たようなことをすることを期待しています。
Cory House 14

3
この回答は古くなっています。代わりにng-repeat-startを使用してください
iwein

73

AngularJS 1.2にng-repeat-startは、あなたが要求したとおりに動作するという名前のディレクティブがあります。使用方法の説明については、この質問の私の回答参照してください。


Angularは追いついてきました、これは今では適切な解決策です。
iwein 2014年

33

ng> 1.2を使用する場合、ng-repeat-start/end不要なタグを生成せずに使用する例を次に示します。

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script>
      angular.module('mApp', []);
    </script>
  </head>
  <body ng-app="mApp">
    <table border="1" width="100%">
      <tr ng-if="0" ng-repeat-start="elem in [{k: 'A', v: ['a1','a2']}, {k: 'B', v: ['b1']}, {k: 'C', v: ['c1','c2','c3']}]"></tr>

      <tr>
        <td rowspan="{{elem.v.length}}">{{elem.k}}</td>
        <td>{{elem.v[0]}}</td>
      </tr>
      <tr ng-repeat="v in elem.v" ng-if="!$first">
        <td>{{v}}</td>
      </tr>

      <tr ng-if="0" ng-repeat-end></tr>
    </table>
  </body>
</html>

重要なポイント:のために使用されるタグ用ng-repeat-startng-repeat-endセットng-if="0"、ページに挿入されないようにします。このようにして、内部コンテンツはノックアウトjsの場合とまったく同じように処理され(のコマンドを使用<!--...-->)、ガベージは発生しません。


ng-if = "0"を設定すると、一致するng-repeat-endが見つからないというエラーがスローされます。
vikky MCTS 2017

19

コントローラ内のデータをフラット化することをお勧めします。

function MyCtrl ($scope) {
  $scope.myData = [[1,2,3], [4,5,6], [7,8,9]];
  $scope.flattened = function () {
    var flat = [];
    $scope.myData.forEach(function (item) {
      flat.concat(item);
    }
    return flat;
  }
}

そして、HTMLで:

<table>
  <tbody>
    <tr ng-repeat="item in flattened()"><td>{{item}}</td></tr>
  </tbody>
</table>

1
いいえ、ちがいます。ngRepeat式内で関数を呼び出すことは絶対にお勧めしません
Nik

!私の場合、インデックスは= 0各要素の区切りを追加しなければならなかったのng-repeat-startng-repeat-end新しいVARソリューションは、各要素の前に、このセパレーターオブジェクトを追加し、余分な変数を作成することでしたので、私のデザインを破ると反復 <div class="c477_group"> <div class="c477_group_item" ng-repeat="item in itemsWithSeparator" ng-switch="item.id" ng-class="{'-divider' : item.id == 'SEPARATOR'}"> <div class="c478" ng-switch-when="FAS"/> <div class="c478" ng-switch-when="SEPARATOR" /> <div class="c479" ng-switch-default /> </div> </div>
hermeslmを

6

上記は正しいですが、より一般的な答えとしては十分ではありません。私はng-repeatをネストする必要がありましたが、同じhtmlレベルに留まるため、同じ親に要素を記述します。タグ配列には、タグ配列も持つタグが含まれています。実は木です。

[{ name:'name1', tags: [
  { name: 'name1_1', tags: []},
  { name: 'name1_2', tags: []}
  ]},
 { name:'name2', tags: [
  { name: 'name2_1', tags: []},
  { name: 'name2_2', tags: []}
  ]}
]

だからここに私が最終的にやったことです。

<div ng-repeat-start="tag1 in tags" ng-if="false"></div>
    {{tag1}},
  <div ng-repeat-start="tag2 in tag1.tags" ng-if="false"></div>
    {{tag2}},
  <div ng-repeat-end ng-if="false"></div>
<div ng-repeat-end ng-if="false"></div>

開始divと終了divを非表示にするng-if = "false"に注意してください。

印刷する必要があります

name1、name1_1、name1_2、name2、name2_1、name2_2、


1

ただコメントしたいのですが、まだ評判が足りません。そこで、問題を解決する別のソリューションを追加します。この問題を解決するには「最高のハック」ソリューションが必要であるという@bmoeskauの発言に反論したいと思います。この投稿は2歳ですが、最近ディスカッションで取り上げられたので、追加したいと思います自分の2セント:

@btfordが指摘したように、再帰的な構造をリストに変換しようとしているようです。そのため、最初にその構造をリストにフラット化する必要があります。彼の解決策はそれを行いますが、テンプレート内の関数の呼び出しは洗練されていないという意見があります。それが本当である場合(正直、私は知りません)、ディレクティブではなくコントローラーで関数を実行する必要があるだけですか?

どちらの方法でも、htmlにはリストが必要であるため、それをレンダリングするスコープには、そのリストを操作する必要があります。コントローラー内部の構造をフラット化するだけです。$ scope.rows配列を取得したら、単一の単純なng-repeatでテーブルを生成できます。ハッキングやエレガンスがなく、単に機能するように設計された方法です。

Angularsディレクティブには機能性が欠けていません。それらは単に有効なhtmlを書くことを強制します。私の同僚も同様の問題を抱えており、角度のテンプレート化/レンダリング機能に関する批判を支持する@bmoeskauを引用しています。正確な問題を見ると、彼は単純に開始タグを生成し、次にどこか他の場所で終了タグを生成したいと思ったことがわかりました。番号。

構造をリストにフラット化することに関しては、ここに別の解決策があります:

// assume the following structure
var structure = [
    {
        name: 'item1', subitems: [
            {
                name: 'item2', subitems: [
                ],
            }
        ],
    }
];
var flattened = structure.reduce((function(prop,resultprop){
    var f = function(p,c,i,a){
        p.push(c[resultprop]);
        if (c[prop] && c[prop].length > 0 )
          p = c[prop].reduce(f,p);
        return p;
    }
    return f;
})('subitems','name'),[]);

// flattened now is a list: ['item1', 'item2']

これは、サブアイテムを持つツリーのような構造で機能します。プロパティではなくアイテム全体が必要な場合は、平坦化関数をさらに短縮できます。

お役に立てば幸いです。


良いですね。それは直接私を助けませんでしたが、別の解決策に私の心を開きました。どうもありがとう!
Marian Zburlea、2015

0

実際に機能するソリューション

html

<remove  ng-repeat-start="itemGroup in Groups" ></remove>
   html stuff in here including inner repeating loops if you want
<remove  ng-repeat-end></remove>

angular.jsディレクティブを追加する

//remove directive
(function(){
    var remove = function(){

        return {    
            restrict: "E",
            replace: true,
            link: function(scope, element, attrs, controller){
                element.replaceWith('<!--removed element-->');
            }
        };

    };
    var module = angular.module("app" );
    module.directive('remove', [remove]);
}());

簡単な説明のために、

ng-repeatはそれ自体を<remove>要素にバインドし、必要に応じてループします。また、ng-repeat-start / ng-repeat-endを使用しているため、要素だけでなくhtmlのブロックもループします。

次に、カスタム削除ディレクティブは、<remove>開始要素と終了要素を<!--removed element-->


-2
<table>
  <tbody>
    <tr><td>{{data[0].foo}}</td></tr>
    <tr ng-repeat="d in data[1]"><td>{{d.bar}}</td></tr>
    <tr ng-repeat="d in data[2]"><td>{{d.lol}}</td></tr>
  </tbody>
</table>

これは妥当だと思います:)


これは機能しますが、配列に3つの要素がある場合にのみ機能します。
btford 2012

空の配列であっても、配列に3つの要素があることを確認してください(空の配列でのng-repeatは、何もレンダリングしません)。
レナントマルフェルナンデス2012

7
私のポイントは、OPが配列内の可変数の項目に対して機能するソリューションをおそらく必要としているということでした。「この配列には3つの項目が必要」というテンプレートへのハードコーディングは、貧弱な解決策になると思います。
btford 2012

彼の質問の最後のコメントで、彼は要素は同じ構造を持たないので、各構造をハードコーディングすることは避けられないと述べています。
Renan Tomal Fernandes 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.