Cara menggunakan ng-repeat tanpa elemen html

142

Saya perlu menggunakan ng-repeat(dalam AngularJS) untuk mendaftar semua elemen dalam sebuah array.

Komplikasinya adalah bahwa setiap elemen array akan berubah menjadi satu, dua atau tiga baris tabel.

Saya tidak dapat membuat html yang valid, jika ng-repeatdigunakan pada suatu elemen, karena tidak ada jenis elemen berulang yang diizinkan antara <tbody>dan <tr>.

Misalnya, jika saya menggunakan ng-repeat <span>, saya akan mendapatkan:

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

Yang html tidak valid.

Tapi yang perlu saya hasilkan adalah:

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

di mana baris pertama telah dihasilkan oleh elemen array pertama, tiga berikutnya oleh yang kedua dan kelima dan keenam oleh elemen array terakhir.

Bagaimana saya bisa menggunakan ng-repeat sedemikian rupa sehingga elemen html yang terikat 'menghilang' selama rendering?

Atau adakah solusi lain untuk ini?


Klarifikasi: Struktur yang dihasilkan akan terlihat seperti di bawah ini. Setiap elemen array dapat menghasilkan antara 1-3 baris tabel. Jawabannya idealnya mendukung 0-n baris per elemen array.

<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>          
fadedbee
sumber
Mungkin Anda harus menggunakan "replace: true"? Lihat ini: stackoverflow.com/questions/11426114/…
Juga, mengapa Anda tidak bisa menggunakan ng-repeat pada tr itu sendiri?
2
@ Tommy, karena "setiap elemen array akan berubah menjadi satu, dua atau tiga baris tabel". Jika saya menggunakan ng-repeat pada trsaya akan mendapatkan satu baris per elemen array, sejauh yang saya mengerti.
fadedbee
1
Ok aku paham. Tidak bisakah Anda meratakan modelnya sebelum menggunakannya di pengulang?
@Tommy, tidak. 1-3 trs yang dihasilkan oleh satu elemen array tidak memiliki struktur yang sama.
fadedbee

Jawaban:

66

Pembaruan: Jika Anda menggunakan Angular 1.2+, gunakan ng-repeat-start . Lihat jawaban @ jmagnusson.

Kalau tidak, bagaimana kalau Anda mengulangi ng-ngulang? (AFAIK, tidak apa-apa untuk memiliki beberapa <tbody> dalam satu tabel.)

<tbody ng-repeat="row in array">
  <tr ng-repeat="item in row">
     <td>{{item}}</td>
  </tr>
</tbody>
Mark Rajcok
sumber
62
Meskipun ini secara teknis mungkin bekerja, sangat mengecewakan bahwa jawaban untuk kasus penggunaan umum ini adalah bahwa Anda harus menyuntikkan markup sewenang-wenang (jika tidak perlu). Saya memiliki masalah yang sama (grup baris berulang - TR satu header dengan satu atau lebih TR anak, diulang sebagai grup). Sepele dengan mesin templat lain, tampaknya paling tidak cocok dengan Angular.
Brian Moeskau
1
Sepakat. Saya mencoba melakukan pengulangan pada elemen DT + DD. tidak ada cara untuk melakukan itu tanpa menambahkan elemen pembungkus yang tidak valid
David Lin
2
@ DavidvidLin, dalam Angular v1.2 (setiap kali keluar) Anda akan dapat mengulangi beberapa elemen, misalnya, dt dan dd: youtube.com/watch?v=W13qDdJDHp8&t=17m28s
Mark Rajcok
Ini ditangani secara elegan di KnockoutJS menggunakan sintaksis aliran kontrol tanpa wadah: knockoutjs.com/documentation/foreach-binding.html . Berharap melihat Angular segera melakukan hal serupa.
Cory House
3
Jawaban ini sudah usang, gunakan ng-repeat-start sebagai gantinya
iwein
73

Pada AngularJS 1.2 ada arahan ng-repeat-startyang melakukan apa yang Anda minta. Lihat jawaban saya dalam pertanyaan ini untuk deskripsi tentang bagaimana menggunakannya.

jmagnusson
sumber
Angular telah menyusul, ini adalah solusi yang tepat sekarang.
iwein
33

Jika Anda menggunakan ng> 1.2, berikut adalah contoh penggunaan ng-repeat-start/endtanpa membuat tag yang tidak perlu:

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

Poin penting: untuk tag yang digunakan ng-repeat-startdan ng-repeat-enddiatur ng-if="0", agar tidak dimasukkan dalam halaman. Dengan cara ini, konten batin akan ditangani persis seperti di knockoutjs (menggunakan perintah di <!--...-->), dan tidak akan ada sampah.

Duka Árpád
sumber
setelah mengatur ng-if = "0", ia melempar kesalahan yang mengatakan tidak dapat menemukan yang cocok ng-repeat-end.
vikky MCTS
19

Anda mungkin ingin meratakan data dalam pengontrol Anda:

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

Dan kemudian di HTML:

<table>
  <tbody>
    <tr ng-repeat="item in flattened()"><td>{{item}}</td></tr>
  </tbody>
</table>
btford
sumber
1
Bukan itu. Memanggil fungsi di dalam ekspresi ngRepeat sama sekali tidak disarankan
Nik
Dalam kasus saya, saya perlu menambahkan pemisah untuk setiap elemen di mana indeks! = 0 dan ng-repeat-start, ng-repeat-endhancurkan desain saya, jadi solusinya adalah membuat variabel tambahan dengan menambahkan objek pemisah ini sebelum setiap elemen dan mengulangi var baru: <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

Jawaban di atas benar tetapi untuk jawaban yang lebih umum tidak cukup. Saya perlu ng-ngulangi, tapi tetap pada level html yang sama, artinya tulis elemen-elemen di induk yang sama. Array tag berisi tag yang juga memiliki array tag. Sebenarnya itu adalah pohon.

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

Jadi inilah yang akhirnya saya lakukan.

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

Catat ng-if = "false" yang menyembunyikan div awal dan akhir.

Itu harus dicetak

name1, name1_1, name1_2, name2, name2_1, name2_2,

Αλέκος
sumber
1

Saya hanya ingin berkomentar, tetapi reputasi saya masih kurang. Jadi saya menambahkan solusi lain yang memecahkan masalah juga. Saya benar-benar ingin menyangkal pernyataan yang dibuat oleh @bmoeskau bahwa penyelesaian masalah ini memerlukan solusi 'hacky at best', dan karena ini muncul baru-baru ini dalam diskusi meskipun posting ini berumur 2 tahun, saya ingin menambahkan memiliki dua sen:

Seperti @btford tunjukkan, Anda tampaknya mencoba mengubah struktur rekursif menjadi daftar, jadi Anda harus meratakan struktur itu menjadi daftar terlebih dahulu. Solusinya melakukan itu, tetapi ada pendapat yang menyebut fungsi di dalam templat tidak tepat. jika itu benar (jujur, saya tidak tahu) tidakkah itu hanya memerlukan menjalankan fungsi di controller daripada direktif?

Bagaimanapun, html Anda memerlukan daftar, sehingga ruang lingkup yang membuatnya harus memiliki daftar untuk bekerja dengannya. Anda hanya perlu meratakan struktur di dalam pengontrol Anda. setelah Anda memiliki array $ scope.rows, Anda dapat menghasilkan tabel dengan satu, ng-repeat sederhana. Tidak ada peretasan, tidak ada kekurangan, hanya cara itu dirancang untuk bekerja.

Arahan arahan tidak kurang fungsional. Mereka hanya memaksa Anda untuk menulis html yang valid. Seorang kolega saya memiliki masalah yang sama, mengutip @bmoeskau untuk mendukung kritik atas fitur templar / rendering yang dibuat oleh angulars. Ketika melihat masalah yang tepat, ternyata dia hanya ingin membuat tag terbuka, lalu menutup tag di tempat lain, dll. Sama seperti di masa lalu ketika kita akan menggabungkan html kita dari string .. benar? tidak.

untuk meratakan struktur menjadi daftar, inilah solusi lain:

// 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']

ini akan berfungsi untuk struktur seperti pohon yang memiliki sub item. Jika Anda menginginkan keseluruhan item alih-alih properti, Anda dapat lebih mempersingkat fungsi perataan.

berharap itu bisa membantu.

Ar Es
sumber
Bagus Meskipun tidak membantu saya secara langsung, itu membuka pikiran saya untuk solusi lain. Terima kasih banyak!
Marian Zburlea
0

untuk solusi yang benar-benar berfungsi

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>

tambahkan direktif 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]);
}());

untuk penjelasan singkat,

ng-repeat mengikat dirinya sendiri ke <remove>elemen dan loop sebagaimana mestinya, dan karena kami telah menggunakan ng-repeat-start / ng-repeat-end itu loop blok html bukan hanya elemen.

lalu kustom menghapus arahan menempatkan elemen <remove>mulai dan selesai dengan<!--removed element-->

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

Saya pikir ini valid :)

Renan Tomal Fernandes
sumber
Meskipun ini berfungsi, itu hanya akan berfungsi jika array memiliki tiga elemen.
btford
Pastikan saja bahwa array memiliki 3 elemen, bahkan jika mereka array kosong (ng-ulangi dengan array kosong tidak membuat apa-apa).
Renan Tomal Fernandes
7
Maksud saya adalah bahwa OP mungkin menginginkan solusi yang berfungsi untuk sejumlah variabel item dalam array. Saya berasumsi hardcoding "harus ada tiga item dalam array ini" ke dalam template akan menjadi solusi yang buruk.
btford
Dalam komentar terakhir pada pertanyaannya, dia mengatakan bahwa elemen tidak akan memiliki struktur yang sama, sehingga hardcoding setiap struktur tidak bisa dihindari.
Renan Tomal Fernandes