Zugriffsschlüssel und Wert des Objekts mit * ngFor

425

Ich bin ein bisschen verwirrt darüber, wie man das keyund valueeines Objekts in angle2 während der Verwendung erhält*ngFor zum Iterieren über das Objekt verwendet. Ich weiß, dass es in Angular 1.x eine Syntax wie gibt

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

aber ich weiß nicht, wie ich das gleiche in angle2 machen soll. Ich habe etwas Ähnliches ohne Erfolg versucht:

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

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

Hier ist ein Plnkr mit meinem Versuch: http://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

Wie kann ich key1und key2dynamisch mit *ngFor? Nachdem ich ausgiebig gesucht hatte, kam mir die Idee, Rohre zu verwenden, aber ich weiß nicht, wie ich das anstellen soll. Gibt es ein eingebautes Rohr, um dasselbe in angle2 zu tun?

Pardeep Jain
quelle
2
Derzeit gibt es keine Unterstützung key, valuePaar Art von Syntax in Angular2 ngFor, sollten Sie sich diese Antwort
Pankaj Parkar
@PankajParkar ja habe diese Antwort schon gelesen. eine Alternative für jetzt?
Pardeep Jain
@ Pradeep Ich denke jetzt nicht über einen anderen Weg nach, du solltest dafür einen eigenen erstellen Pipe.
Pankaj Parkar
hmm aber ich habe keine ahnung wie man ein rohr dafür erstellt.
Pardeep Jain
@ Pradeep Antwort, die ich Ihnen als Referenz gegeben habe, hat diese Implementierung. sie sollten arbeiten ..
Pankaj Parkar

Antworten:

398

Haben Object.keyszugänglich in der Vorlage und verwenden Sie es in *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(){}
}
Tomtastico
quelle
1
@PardeepJain es ist eine ES5-Methode developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
tomtastico
25
Dies ist eine bessere und effizientere Lösung
Aous1000
1
@tomtastico Wie würden Sie dies für ein 3D-Array anzeigen? Zum Beispiel {"1": {"1.1": ["1.1.1", "1.1.2"]}}. Und dann 3 ngFor's
Frank
@ Frank du hast es gerade selbst gesagt. Nest die *ngFors. Die ersten beiden verwenden objectKeys, innerlich keine Notwendigkeit (da es sich nur um ein Array handelt).
Tomtastico
1
Genial. Das Setzen von objectKeys = Object.keys ist die einfachste Methode, die ich gesehen habe, um die Länge eines Objekts anhand des HTML-Codes zu überprüfen.
JAC
391

Wie in der neuesten Version von Angular (v6.1.0) hat Angular Team eine neue integrierte Pipe mit dem gleichen Namen wie keyvaluePipe hinzugefügt, mit der Sie Objekte, Karten und Arrays im commonModul des Angular-Pakets durchlaufen können . Beispielsweise -

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

Arbeitsgabel Beispiel

Weitere nützliche Informationen finden Sie hier -

Wenn Sie Angular v5 oder niedriger verwenden oder Pipe verwenden möchten, befolgen Sie diese Antwort

Pardeep Jain
quelle
1
lol Ich musste ein ng6-Update machen, um auf diese Pipe zuzugreifen - tolles Zeug - thx
danday74
36
Sie können die ursprüngliche Schlüsselreihenfolge mit einem benutzerdefinierten Komparator *ngFor="let item of testObject | keyvalue:keepOriginalOrder"public keepOriginalOrder = (a, b) => a.key
beibehalten
2
public keepOriginalOrder = (a, b) => a.key thx viel dafür
Kumaresan Perumal
1
Dies sollte die Antwort sein - gut funktionieren auf eckigen 7
Calios
1
Unglaublich, dass dies seit der ersten Version nicht mehr da draußen war
Carlos Pinzón
227

Sie können eine benutzerdefinierte Pipe erstellen, um die Liste der Schlüssel für jedes Element zurückzugeben. So ähnlich:

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

und benutze es so:

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

Bearbeiten

Sie können auch einen Eintrag zurückgeben, der sowohl Schlüssel als auch Wert enthält:

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

und benutze es so:

<span *ngFor="let entry of content | keys">           
  Key: {{entry.key}}, value: {{entry.value}}
</span>
Thierry Templier
quelle
1
Beachten Sie die fehlende schließende Klammer inkeys.push({key: key, value: value[key]);
Marton Pallagi
49
Ich rate eigentlich jedem davon ab, Pipes zu verwenden, um Sammlungen innerhalb von *ngForexpression zu erstellen . Dies führt zu einem enormen Leistungsengpass, da die Sammlung jedes Mal generiert werden muss, wenn der Änderungsdetektor nach Änderungen sucht.
Martin
3
Vielen Dank für die Lösung ... das Problem ist, dass die Pipe nicht aktualisiert wird, wenn sich das Objekt ändert. Wenn ich pure:falsedas Rohr hinzufüge , wird es sehr ineffizient. Haben Sie eine Lösung, um die Pipe manuell zu aktualisieren, wenn ich das Objekt ändere (Element entfernen)?
Ncohen
4
Die Antwort ist etwas veraltet. Die Zeile * ngFor = "# entry of content | keys" funktioniert nicht richtig und die for ... in-Schleife sollte besser in "for (const key of Object.keys (value))" geändert werden
Experimenter
2
@RachChen Nicht in Vorlagen: 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

Aktualisieren

In 6.1.0-beta.1 KeyValuePipe eingeführt https://github.com/angular/angular/pull/24319

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

Plunker Beispiel

Vorherige Version

Ein anderer Ansatz besteht darin, eine NgForInDirektive zu erstellen , die wie folgt verwendet wird:

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

Plunker Beispiel

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);
    }
  }
}
Yurzui
quelle
42

Ab Angular 6.1 können Sie die Keyvalue- Pipe verwenden:

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

Es ist jedoch unpraktisch, die resultierende Liste nach dem Schlüsselwert zu sortieren. Wenn Sie etwas Neutrales brauchen:

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

Vergessen Sie nicht, das Attribut pure: false Pipe anzugeben . In diesem Fall wird die Pipe bei jedem Änderungserkennungszyklus aufgerufen, auch wenn sich die Eingabereferenz nicht geändert hat (dies ist der Fall, wenn Sie einem Objekt Eigenschaften hinzufügen).

Gerard Carbó
quelle
Bereits die gleiche Antwort über stackoverflow.com/a/51491848/5043867
Pardeep Jain
26

Ausarbeitung der Antwort von @ Thierry anhand eines Beispiels.

Es gibt keine eingebaute Pipe oder Methode, um key and valueaus der * ngFor-Schleife zu gelangen. Daher müssen wir für dasselbe eine benutzerdefinierte Pipe erstellen. Wie Thierry hier sagte, ist die Antwort mit Code.

** Die Pipe-Klasse implementiert die Transformationsmethode der PipeTransform-Schnittstelle, die einen Eingabewert und ein optionales Array von Parameterzeichenfolgen verwendet und den transformierten Wert zurückgibt.

** Die Transformationsmethode ist für eine Pipe unerlässlich. Die PipeTransform-Schnittstelle definiert diese Methode und führt sowohl das Tooling als auch den Compiler. Es ist optional; Angular sucht und führt die Transformationsmethode unabhängig davon aus. Weitere Informationen zu Rohren finden Sie hier

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

und HTML-Teil ist:

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

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

Update auf RC

wie von user6123723 (danke) im Kommentar vorgeschlagen, ist hier ein Update.

<ul>
  <li *ngFor='let key of demo | keys'>
   Key: {{key.key}}, value: {{key.value}}
  </li>
</ul>
Pardeep Jain
quelle
Dies muss aktualisiert werden: Hier ist die Warnung, dass "#" in Ausdrücken angezeigt wird, veraltet. Verwenden Sie stattdessen "let"! ("</ li> -> <ul * ngIf =" demo "> <li [ERROR ->] * ngFor = '# Schlüssel der Demo | Schlüssel'> Schlüssel: {{key.key}}, Wert: { {key.value}} </ li> "): myComponent @ 56: 6
user6123723
Ich bin mir nicht sicher, ob dies neu ist, aber um aus den Dokumenten zu zitieren:> Wir müssen unsere Pipe in das Deklarationsarray des AppModule aufnehmen.
Akzidenzgrotesk
18

@Marton hatte einen wichtigen Einwand gegen die akzeptierte Antwort mit der Begründung, dass die Pipe bei jeder Änderungserkennung eine neue Sammlung erstellt. Ich würde stattdessen einen HtmlService erstellen, der eine Reihe von Dienstprogrammfunktionen bereitstellt, die die Ansicht wie folgt verwenden kann:

@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...
}
Stephen Paul
quelle
2
und wie ist das besser als nur Object.keys(...)im * ngFor?
Simon_Weaver
8
Weil es werfen wird : TypeError: Cannot read property 'keys' of undefined. Es scheint in der Vorlage nicht unterstützt zu werden.
Stephen Paul
1
Dies funktioniert sehr gut als Lösung und vermeidet die oben genannten Leistungsprobleme. stackoverflow.com/questions/35534959/…
J. Adam Connor
Hallo, kann dieses b nicht in der templateOption verwendet werden, sondern im tatsächlichen HTML-Code der Vorlage? danke
Scaramouche
16

Wenn Sie Lodash bereits verwenden, können Sie diesen einfachen Ansatz ausführen, der sowohl Schlüssel als auch Wert umfasst:

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

Fügen Sie in die Typoskriptdatei Folgendes ein:

import * as _ from 'lodash';

und in der exportierten Komponente enthalten:

_: any = _;
Jeremy Moritz
quelle
Entschuldigung, aber keine Notwendigkeit, eine zusätzliche Bibliothek wie Lodash für solche Dinge zu verwenden. sowieso sind neue Methoden immer willkommen :)
Pardeep Jain
8

Vielen Dank für die Pipe, aber ich musste einige Änderungen vornehmen, bevor ich sie in Angular 2 RC5 verwenden konnte. Die Pipe-Importzeile wurde geändert und der Schlüsselarray-Initialisierung wurde ein beliebiger Typ hinzugefügt.

 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;
  }
}
Adam Winnipass
quelle
Ja, die Importe wurden geändert
Pardeep Jain
7

Keine der Antworten hier hat sofort für mich funktioniert. Hier ist, was für mich funktioniert hat:

pipes/keys.tsMit Inhalten erstellen :

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

Hinzufügen zu app.module.ts(Ihrem Hauptmodul):

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

und fügen Sie dann Ihrem Moduldeklarationsarray Folgendes hinzu:

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

Dann können Sie in Ihrer Ansichtsvorlage Folgendes verwenden:

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

Hier ist eine gute Referenz, die ich gefunden habe, wenn Sie mehr lesen möchten.

Cjohansson
quelle
Darf ich wissen, was der Unterschied zwischen Ihrer Antwort und anderen Antworten (nur mit Pipe) ist? es scheint dasselbe wie oben
Pardeep Jain
1
Sicher 1. In den obigen Beispielen wird * ngFor = "# entry" anstelle von * ngFor = "let entry of" verwendet, und mein Compiler hat die Syntax #entry nicht akzeptiert. Die Referenz verwendet auch # nicht. "let entry of (myData | keys)" scheint eine bessere Lösung zu sein. 2. Mein Compiler hat die Beispiel-Pipe-Klasse auch nicht validiert, da explizite Datentypen fehlten, also habe ich das hinzugefügt. 3. Die obigen Beispiele zeigen nicht, wie die Pipe in ein Projekt integriert wird, wie es meine Antwort tut. Sie müssen es in das Hauptmodul importieren.
Cjohansson
haha ja natürlich, denn als die Antwort zu diesem Zeitpunkt Syntax einschließlich #usw. gegeben wurde, ist Ihre Antwort zweifellos auch richtig
Pardeep Jain
6

Es gibt eine wirklich schöne Bibliothek, die dies unter anderen schönen Pfeifen tut. Es heißt ngx-pipes .

Beispielsweise gibt die Schlüsselpipe Schlüssel für ein Objekt und die Wertepipe Werte für ein Objekt zurück:

Schlüsselrohr

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

Werte Rohr

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

Sie müssen keine eigene Pipe erstellen :)

RichieRock
quelle
2
gute Alternative, aber die Sache ist, warum externe Bibliothek für einfache Code-Ruhe zu verwenden, wenn wir dies mit einfachen Code wie Pipe tun können
Pardeep Jain
2
Ähm ... aber es ist eine Pfeife? Es ist nur eine Zeile in Ihrer package.json und zwei weitere Zeilen in Ihrem Modul, wenn Sie die Bibliothek importieren. Andererseits benötigt eine benutzerdefinierte Pipe eine separate Datei mit etwa 10 bis 20 Codezeilen sowie die Importzeilen in Ihrem Modul. Wir finden die Verwendung von ngx-Pipes in unseren Projekten sehr einfach. Warum sollten wir das Rad neu erfinden? :)
RichieRock
Ja, kein Zweifel, eigentlich ist es eine meinungsbasierte, Sie können zwischen diesen beiden wählen, niemand ist falsch.
Pardeep Jain
2
Vergessen Sie nicht, wenn Sie eine benutzerdefinierte Rohr schreiben, müssen Sie das benutzerdefinierte Rohr testen , wie gut . Das sind also 10 bis 20 Zeilen Rohrleitungscode und dann wahrscheinlich 20 bis 40 Zeilen Testcode zum Testen der Rohrleitung.
Danielwell
4

Index verwenden:

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

Verwendungszweck:

{{key}} -> {{value}}
Adonias Vasquez
quelle
1
Das ist etwas Neues für mich. Besser, wenn Sie zusammen mit Ihrer Antwort ein Beispiel hinzufügen könnten :) Können Sie mich auch auf eine Dokumentation dafür verweisen?
Pardeep Jain
Was ist die Art von Objekten? Array oder Karte? Bitte machen Sie es klar. Vielen Dank im Voraus
Basil Mohammed
3

Hier ist die einfache Lösung

Hierfür können Sie Typoskript-Iteratoren verwenden

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

Sudheer KB
quelle
3

Ändern Sie den Demo-Typ in Array oder iterieren Sie über Ihr Objekt und verschieben Sie es in ein anderes Array

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

und von html:

<div *ngFor="obj of details">
  <p>{{obj.key}}</p>
  <p>{{obj.value}}</p>
  <p></p>
</div>
Mohammad Reza Mrg
quelle
Dies ist keine geeignete Methode, dies kann von jedem leicht durchgeführt werden.
Pardeep Jain
1

Ich denke, Object.keys ist die beste Lösung für dieses Problem. Für alle, die auf diese Antwort stoßen und herausfinden möchten, warum Object.keys ihnen ['0', '1'] anstelle von ['key1', 'key2'] gibt, ist eine warnende Geschichte - achten Sie auf den Unterschied zwischen " von "und" in ":

Ich habe bereits Object.keys verwendet, etwas Ähnliches:

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

Anstelle von

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

Ich hatte versehentlich geschrieben

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

das "funktionierte" einwandfrei ohne Fehler und kehrte zurück

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

Das hat mich ungefähr 2 Stunden gekostet, zu googeln und zu fluchen.

(schlägt auf die Stirn)

Ciaran Bruen
quelle
1

Sie können den Schlüssel eines dynamischen Objekts erhalten, indem Sie dies versuchen

myObj['key']
Rizo
quelle
0

Sie müssen es vorerst so machen, ich weiß nicht sehr effizient, da Sie das Objekt, das Sie von Firebase erhalten, nicht konvertieren möchten.

    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;

    });
Kevinius
quelle