kunci akses dan nilai objek menggunakan * ngFor


426

Saya agak bingung tentang cara mendapatkan keydan valuedari suatu objek di angular2 saat menggunakan *ngForuntuk iterasi objek. Saya tahu di sudut 1.x ada sintaksis seperti

ng-repeat="(key, value) in demo"

tapi saya tidak tahu bagaimana melakukan hal yang sama di angular2. Saya sudah mencoba yang serupa, tanpa hasil:

<ul>
  <li *ngFor='#key of demo'>{{key}}</li>
</ul>

demo = {
    'key1': [{'key11':'value11'}, {'key12':'value12'}],
    'key2': [{'key21':'value21'}, {'key22':'value22'}],
  }

Berikut ini adalah plnkr dengan upaya saya: http://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

Bagaimana saya bisa mendapatkan key1dan key2menggunakan secara dinamis *ngFor? Setelah mencari secara ekstensif, saya menemukan ide untuk menggunakan pipa tetapi saya tidak tahu bagaimana cara melakukannya. Apakah ada pipa inbuilt untuk melakukan hal yang sama di angular2?


2
Saat ini tidak ada key, valuepasangan dukungan jenis sintaks di angular2 ngFor, Anda harus melihat jawaban ini
Pankaj Parkar

@PankajParkar ya sudah membaca jawaban ini. ada alternatif untuk saat ini?
Pardeep Jain

@Pradeep Saya tidak memikirkan cara lain untuk ini sekarang, Anda harus pergi untuk membuat sendiri Pipeuntuk ini ..
Pankaj Parkar

hmm tetapi saya tidak tahu cara membuat pipa untuk hal yang sama.
Pardeep Jain

@Pradeep jawaban yang saya berikan kepada Anda untuk referensi, memiliki implementasi itu. mereka harus bekerja ..
Pankaj Parkar

Jawaban:


399

Dapat Object.keysdiakses di templat dan menggunakannya *ngFor.

@Component({
  selector: 'app-myview',
  template: `<div *ngFor="let key of objectKeys(items)">{{key + ' : ' + items[key]}}</div>`
})

export class MyComponent {
  objectKeys = Object.keys;
  items = { keyOne: 'value 1', keyTwo: 'value 2', keyThree: 'value 3' };
  constructor(){}
}


25
Ini adalah solusi yang lebih baik dan lebih efisien
Aous1000

1
@tomtastico Bagaimana Anda menampilkan ini untuk array 3D? Misalnya {"1": {"1.1": ["1.1.1", "1.1.2"]}}. Dan kemudian bersarang 3 ngFor
Frank

@ Frank, Anda baru saja mengatakannya sendiri. Sarang *ngFor. Dua yang pertama menggunakan objectKeys, terdalam tidak perlu (karena itu hanya sebuah array).
tomtastico

1
Luar biasa. Pengaturan objectKeys = Object.keys adalah metode paling sederhana yang saya lihat untuk dapat memeriksa panjang suatu objek dari HTML.
JAC

394

Seperti dalam rilis terbaru Angular (v6.1.0) , Tim Angular telah menambahkan pipa built in baru untuk pipa yang sama dengan nama keyvalueuntuk membantu Anda beralih melalui objek, peta, dan array, dalam commonmodul paket sudut. Sebagai contoh -

<div *ngFor="let item of testObject | keyvalue">
    Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>

Contoh Forked Bekerja

lihat di sini untuk informasi yang lebih berguna -

Jika Anda menggunakan V5 sudut atau di bawah atau Anda ingin mencapai menggunakan pipa ikuti jawaban ini


1
lol Saya harus melakukan pembaruan ng6 hanya untuk mengakses pipa ini - hal-hal hebat - thx
danday74

36
Anda dapat menjaga urutan kunci asli menggunakan pembanding khusus: *ngFor="let item of testObject | keyvalue:keepOriginalOrder" dan di kelas Anda tentukan: public keepOriginalOrder = (a, b) => a.key
mike-shtil

2
public keepOriginalOrder = (a, b) => a.key thx banyak untuk ini
Kumaresan Perumal

1
ini harus menjadi jawaban - bekerja dengan baik pada sudut 7
calios

1
Tidak dapat dipercaya ini tidak ada sejak versi pertama
Carlos Pinzón

227

Anda bisa membuat pipa kustom untuk mengembalikan daftar kunci untuk setiap elemen. Sesuatu seperti itu:

import { PipeTransform, Pipe } from '@angular/core';

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push(key);
    }
    return keys;
  }
}

dan gunakan seperti itu:

<tr *ngFor="let c of content">           
  <td *ngFor="let key of c | keys">{{key}}: {{c[key]}}</td>
</tr>

Edit

Anda juga bisa mengembalikan entri yang berisi kunci dan nilai:

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push({key: key, value: value[key]});
    }
    return keys;
  }
}

dan gunakan seperti itu:

<span *ngFor="let entry of content | keys">           
  Key: {{entry.key}}, value: {{entry.value}}
</span>

1
perhatikan braket penutup yang hilang dikeys.push({key: key, value: value[key]);
Marton Pallagi

49
Saya sebenarnya mencegah siapa pun dari menggunakan pipa untuk membuat koleksi *ngForekspresi di dalam . Ini menciptakan hambatan kinerja yang sangat besar karena harus menghasilkan koleksi setiap kali detektor perubahan memeriksa perubahan.
martin

3
Terima kasih atas solusinya ... masalahnya adalah bahwa setiap kali objek berubah, pipa tidak diperbarui. Jika saya tambahkan pure:falseke pipa, itu menjadi sangat tidak efisien. Apakah Anda memiliki solusi untuk memperbarui pipa secara manual setiap kali saya mengubah objek (menghapus item)?
ncohen

4
Jawabannya agak ketinggalan jaman. Baris * ngFor = "# entri konten | kunci" tidak berfungsi dengan baik dan untuk ... dalam lingkaran lebih baik untuk mengubah ke "untuk (kunci const dari Object.keys (nilai))"
Eksperimen

2
@RachChen Tidak dalam templat: common: NgFor has been removed as it was deprecated since v4. Use NgForOf instead. This does not impact the use of*ngFor in your templates.( jaxenter.com/road-to-angular-5-133253.html )
mwld

49

Memperbarui

Dalam 6.1.0-beta.1 KeyValuePipe diperkenalkan https://github.com/angular/angular/pull/24319

<div *ngFor="let item of {'b': 1, 'a': 1} | keyvalue">
  {{ item.key }} - {{ item.value }}
</div>

Contoh Plunker

Versi sebelumnya

Pendekatan lain adalah membuat NgForInarahan yang akan digunakan seperti:

<div *ngFor="let key in obj">
   <b>{{ key }}</b>: {{ obj[key] }}
</div>

Contoh Plunker

ngforin.directive.ts

@Directive({
  selector: '[ngFor][ngForIn]'
})
export class NgForIn<T> extends NgForOf<T> implements OnChanges {

  @Input() ngForIn: any;

  ngOnChanges(changes: NgForInChanges): void {
    if (changes.ngForIn) {
      this.ngForOf = Object.keys(this.ngForIn) as Array<any>;

      const change = changes.ngForIn;
      const currentValue = Object.keys(change.currentValue);
      const previousValue = change.previousValue ? Object.keys(change.previousValue) : undefined;
      changes.ngForOf =  new SimpleChange(previousValue, currentValue, change.firstChange);

      super.ngOnChanges(changes);
    }
  }
}

43

Dari Angular 6.1 Anda dapat menggunakan pipa keyvalue :

<div *ngFor="let item of testObject | keyvalue">
    Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>

Tetapi memiliki ketidaknyamanan yang mengurutkan daftar yang dihasilkan oleh nilai kunci. Jika Anda membutuhkan sesuatu yang netral:

@Pipe({ name: 'keyValueUnsorted', pure: false  })
export class KeyValuePipe implements PipeTransform {
  transform(input: any): any {
    let keys = [];
    for (let key in input) {
      if (input.hasOwnProperty(key)) {
        keys.push({ key: key, value: input[key]});
      }
    }
    return keys;
  }
}

Jangan lupa untuk menentukan atribut pure: false pipe. Dalam kasus ini, pipa dipanggil pada setiap siklus deteksi perubahan, bahkan jika referensi input tidak berubah (demikian juga halnya ketika Anda menambahkan properti ke objek).


Sudah berbagi jawaban yang sama di atas stackoverflow.com/a/51491848/5043867
Pardeep Jain

26

Elaborasi jawaban @ Thierry dengan contoh.

Tidak ada pipa atau metode inbuilt untuk mendapatkan key and valuedari loop * ngFor. jadi kita harus membuat pipa kustom untuk hal yang sama. seperti kata thierry di sini adalah jawabannya dengan kode.

** Kelas pipa mengimplementasikan metode transformasi antarmuka PipeTransform yang mengambil nilai input dan array opsional string parameter dan mengembalikan nilai yang diubah.

** Metode transformasi sangat penting untuk pipa. Antarmuka PipeTransform mendefinisikan metode itu dan memandu baik tooling maupun compiler. Itu opsional; Angular mencari dan mengeksekusi metode transformasi. untuk info lebih lanjut salam pipa lihat di sini

import {Component, Pipe, PipeTransform} from 'angular2/core';
import {CORE_DIRECTIVES, NgClass, FORM_DIRECTIVES, Control, ControlGroup, FormBuilder, Validators} from 'angular2/common';

@Component({
    selector: 'my-app',
    templateUrl: 'mytemplate.html',
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES],
    pipes: [KeysPipe]
})
export class AppComponent { 

  demo = {
    'key1': 'ANGULAR 2',
    'key2': 'Pardeep',
    'key3': 'Jain',
  }
}


@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push({key: key, value: value[key]});
    }
    return keys;
  }
}

dan bagian HTML adalah:

<ul>
  <li *ngFor='#key of demo | keys'>
   Key: {{key.key}}, value: {{key.value}}
  </li>
</ul>

Bekerja Plnkr http://plnkr.co/edit/50LlK0k6OnMnkc2kNHM2?p=preview

perbarui ke RC

seperti yang disarankan oleh user6123723 (terima kasih) di komentar di sini adalah pembaruan.

<ul>
  <li *ngFor='let key of demo | keys'>
   Key: {{key.key}}, value: {{key.value}}
  </li>
</ul>

Ini perlu diperbarui: Inilah peringatan yang saya dapatkan "#" di dalam ekspresi sudah usang. Gunakan "biarkan" saja! ("</li> -> <ul * ngIf =" demo "> <li [GALAT ->] * ngFor = '# kunci demo | kunci'> Kunci: {{key.key}}, nilai: { {key.value}} </li> "): myComponent @ 56: 6
user6123723

Tidak yakin apakah ini baru, tetapi mengutip dari dokumen:> Kita harus memasukkan pipa kami dalam larik deklarasi AppModule.
Akzidenzgrotesk

18

@ Marton memiliki keberatan penting terhadap jawaban yang diterima dengan alasan bahwa pipa membuat koleksi baru pada setiap deteksi perubahan. Saya lebih suka membuat HtmlService yang menyediakan berbagai fungsi utilitas yang dapat digunakan tampilan sebagai berikut:

@Component({
  selector: 'app-myview',
  template: `<div *ngFor="let i of html.keys(items)">{{i + ' : ' + items[i]}}</div>`
})
export class MyComponent {
  items = {keyOne: 'value 1', keyTwo: 'value 2', keyThree: 'value 3'};
  constructor(private html: HtmlService){}
}

@Injectable()
export class HtmlService {
  keys(object: {}) {
    return Object.keys(object);
  }
  // ... other useful methods not available inside html, like isObject(), isArray(), findInArray(), and others...
}

2
dan bagaimana itu lebih baik daripada hanya Object.keys(...)di dalam * ngFor?
Simon_Weaver

8
Karena akan membuang: TypeError: Cannot read property 'keys' of undefined. Tampaknya tidak didukung dalam templat.
Stephen Paul

1
Ini berfungsi sangat baik sebagai solusi dan menghindari masalah kinerja yang disebutkan di atas. stackoverflow.com/questions/35534959/…
J. Adam Connor

halo, bisakah ini b tidak digunakan dalam templateopsi, tetapi dalam kode html aktual template? terima kasih
Scaramouche

16

Jika Anda sudah menggunakan Lodash, Anda dapat melakukan pendekatan sederhana ini yang mencakup kunci dan nilai:

<ul>
  <li *ngFor='let key of _.keys(demo)'>{{key}}: {{demo[key]}}</li>
</ul>

Dalam file naskah, sertakan:

import * as _ from 'lodash';

dan dalam komponen yang diekspor, meliputi:

_: any = _;

maaf tapi tidak perlu menggunakan perpustakaan tambahan seperti Lodash untuk hal-hal seperti itu. lagian metode baru selalu diterima :)
Pardeep Jain

8

Terima kasih untuk pipa tetapi saya harus membuat beberapa perubahan sebelum saya bisa menggunakannya di sudut 2 RC5. Mengubah jalur impor Pipa dan juga menambahkan jenis apa saja ke inisialisasi array kunci.

 import {Pipe, PipeTransform} from '@angular/core';

 @Pipe({name: 'keys'})
 export class KeysPipe implements PipeTransform {
 transform(value) {
   let keys:any = [];
   for (let key in value) {
      keys.push( {key: key, value: value[key]} );
    }
     return keys;
  }
}

ya impor telah diubah
Pardeep Jain

7

Tidak ada jawaban di sini yang berfungsi untuk saya di luar kotak, inilah yang bekerja untuk saya:

Buat pipes/keys.tsdengan konten:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform
{
    transform(value:any, args:string[]): any {
        let keys:any[] = [];
        for (let key in value) {
            keys.push({key: key, value: value[key]});
        }
        return keys;
    }
}

Tambahkan ke app.module.ts(modul utama Anda):

import { KeysPipe } from './pipes/keys';

dan kemudian tambahkan ke array deklarasi modul Anda sesuatu seperti ini:

@NgModule({
    declarations: [
        KeysPipe
    ]
})
export class AppModule {}

Kemudian dalam template tampilan Anda, Anda dapat menggunakan sesuatu seperti ini:

<option *ngFor="let entry of (myData | keys)" value="{{ entry.key }}">{{ entry.value }}</option>

Ini adalah referensi bagus yang saya temukan jika Anda ingin membaca lebih lanjut.


bolehkah saya tahu apa perbedaan antara jawaban Anda dan jawaban lain (hanya menggunakan pipa) yang disediakan di atas? sepertinya sama seperti di atas
Pardeep Jain

1
Tentu 1. Contoh di atas menggunakan * ngFor = "# entry" bukan * ngFor = "biarkan entri" dan kompiler saya tidak menerima sintaks #entry, referensi tidak menggunakan # baik. "biarkan entri (myData | keys)" tampaknya menjadi solusi yang lebih baik. 2. Kompiler saya tidak memvalidasi kelas Pipa baik karena tidak ada tipe data eksplisit jadi saya menambahkannya. 3. Contoh di atas tidak menunjukkan bagaimana mengintegrasikan Pipa ke dalam proyek yang jawaban saya lakukan, Anda perlu mengimpornya ke modul utama.
cjohansson

haha ya offcourese, karena ketika jawaban diberikan pada saat itu termasuk sintaks #dll. btw jawaban Anda juga benar tidak diragukan
Pardeep Jain

6

Ada perpustakaan nyata yang bagus yang melakukan ini di antara pipa bagus lainnya. Ini disebut pipa ngx .

Misalnya, kunci pipa mengembalikan kunci untuk suatu objek, dan nilai pipa mengembalikan nilai untuk objek:

pipa kunci

<div *ngFor="let key of {foo: 1, bar: 2} | keys">{{key}}</div> 
<!-- Output: 'foo' and 'bar -->

nilai pipa

<div *ngFor="let value of {foo: 1, bar: 2} | values">{{value}}</div>
<!-- Output: 1 and 2 -->

Tidak perlu membuat pipa kustom Anda sendiri :)


2
alternatif yang baik, tetapi masalahnya adalah mengapa menggunakan perpustakaan eksternal untuk ketenangan kode sederhana jika kita bisa melakukan ini menggunakan potongan kode sederhana seperti pipa
Pardeep Jain

2
Umm ... tapi itu pipa? Itu hanya satu baris di package.json Anda dan dua baris lain di modul Anda ketika Anda mengimpor perpustakaan. Di sisi lain, pipa kustom membutuhkan file terpisah dengan 10-20 baris kode dan juga baris impor di modul Anda. Kami menemukan menggunakan pipa ngx sangat mudah dalam proyek kami. Mengapa kita harus menemukan kembali roda? :)
RichieRock

ya tidak diragukan lagi, sebenarnya berdasarkan pendapat, Anda dapat memilih antara keduanya, tidak ada yang salah.
Pardeep Jain

2
Jangan lupa, jika Anda menulis pipa kustom, Anda harus menguji pipa kustom itu juga . Jadi itu 10-20 baris kode pipa, dan mungkin 20-40 baris kode uji untuk menguji pipa.
danwellman

4

Gunakan indeks:

<div *ngFor="let value of Objects; index as key">

Pemakaian:

{{key}} -> {{value}}

1
Itu adalah sesuatu yang baru bagi saya, Lebih Baik Jika Anda dapat menambahkan contoh bersama dengan jawaban Anda :) Juga dapatkah Anda mengarahkan saya ke dokumentasi yang sama?
Pardeep Jain

Apa jenis objek? Array atau Peta? Tolong jelaskan. Terima kasih sebelumnya
Basil Mohammed

3

Ini solusi sederhana

Anda dapat menggunakan iterator naskah untuk ini

import {Component} from 'angular2/core';
declare var Symbol;
@Component({
    selector: 'my-app',
    template:`<div>
    <h4>Iterating an Object using Typescript Symbol</h4><br>
Object is : <p>{{obj | json}}</p>
</div>
============================<br>
Iterated object params are:
<div *ngFor="#o of obj">
{{o}}
</div>

`
})
export class AppComponent {
  public obj: any = {
    "type1": ["A1", "A2", "A3","A4"],
    "type2": ["B1"],
    "type3": ["C1"],
    "type4": ["D1","D2"]
  };

  constructor() {
    this.obj[Symbol.iterator] =  () => {
          let i =0;

          return {
            next: () => {
              i++;
              return {
                  done: i > 4?true:false,
                  value: this.obj['type'+i]
              }
            }
          }
    };
  }
}

http://plnkr.co/edit/GpmX8g?p=info


3

ubah jenis demo ke array atau beralih di atas objek Anda dan dorong ke array lain

public details =[];   
Object.keys(demo).forEach(key => {
      this.details.push({"key":key,"value":demo[key]);
    });

dan dari html:

<div *ngFor="obj of details">
  <p>{{obj.key}}</p>
  <p>{{obj.value}}</p>
  <p></p>
</div>

Ini bukan metode yang tepat, ini bisa dengan mudah dilakukan oleh siapa saja.
Pardeep Jain

1

Saya pikir Object.keys adalah solusi terbaik untuk masalah ini. Bagi siapa pun yang menemukan jawaban ini dan berusaha mencari tahu mengapa Object.keys memberi mereka ['0', '1'] alih-alih ['key1', 'key2'], sebuah dongeng peringatan - waspadalah perbedaan antara " dari "dan" dalam ":

Saya sudah menggunakan Object.keys, sesuatu yang mirip dengan ini:

interface demo {
    key: string;
    value: string;
}

createDemo(mydemo: any): Array<demo> {
    const tempdemo: Array<demo> = [];

    // Caution: use "of" and not "in"
    for (const key of Object.keys(mydemo)) {
        tempdemo.push(
            { key: key, value: mydemo[key]}
        );
    }

    return tempdemo;
}

Namun, bukannya

for (const key OF Object.keys(mydemo)) {

Saya telah secara tidak sengaja menulis

for (const key IN Object.keys(mydemo)) {

yang "bekerja" dengan sangat baik tanpa kesalahan dan dikembalikan

[{key: '0', value: undefined}, {key: '1', value: undefined}]

Harganya sekitar 2 jam googling dan kutukan ..

(menampar dahi)


1

Anda bisa mendapatkan kunci objek dinamis dengan mencoba ini

myObj['key']

0

Anda harus melakukannya seperti ini untuk saat ini, saya tahu tidak terlalu efisien karena Anda tidak ingin mengonversi objek yang Anda terima dari firebase.

    this.af.database.list('/data/' + this.base64Email).subscribe(years => {
        years.forEach(year => {

            var localYears = [];

            Object.keys(year).forEach(month => {
                localYears.push(year[month])
            });

            year.months = localYears;

        })

        this.years = years;

    });
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.