chiave d'accesso e valore dell'oggetto usando * ngFor


426

Sono un po 'confuso su come ottenere il keye valuedi un oggetto in angular2 mentre lo uso *ngForper iterare sull'oggetto. So che in 1.x angolare c'è una sintassi simile

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

ma non so come fare lo stesso in angular2. Ho provato qualcosa di simile, senza successo:

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

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

Ecco un plnkr con il mio tentativo: http://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

Come posso ottenere key1e key2utilizzare in modo dinamico *ngFor? Dopo aver cercato a fondo, ho trovato l'idea di usare le pipe ma non so come procedere. Esiste un tubo integrato per fare lo stesso in angular2?


2
al momento non esiste una key, valuecoppia di sintassi di supporto in angular2 ngFor, dovresti guardare questa risposta
Pankaj Parkar

@PankajParkar sì già ho letto questa risposta. qualche supplente per ora?
Pardeep Jain,

@Pradeep Non penso a nessun altro modo per questo ora, dovresti andare per crearne uno proprio Pipeper questo ..
Pankaj Parkar

hmm ma non ho idea di come creare pipe per lo stesso.
Pardeep Jain,

La risposta di @Pradeep che ti ho dato come riferimento ha questa implementazione. dovrebbero funzionare ..
Pankaj Parkar,

Risposte:


399

Accedi Object.keysal modello e usalo 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(){}
}


25
Questa è una soluzione migliore e più efficiente
Aous1000

1
@tomtastico Come lo visualizzeresti per un array 3D? Ad esempio {"1": {"1.1": ["1.1.1", "1.1.2"]}}. E poi nidifica 3 ngPer's
Frank

@Frank l'hai appena detto tu stesso. Nest the *ngFors. Primi due che usano objectKeys, non c'è bisogno più interno (dato che è solo un array).
tomtastico,

1
Eccezionale. L'impostazione di objectKeys = Object.keys è il metodo più semplice che ho visto per poter controllare la lunghezza di un oggetto dall'HTML.
JAC,

394

Come nell'ultima versione di Angular (v6.1.0) , Angular Team ha aggiunto una nuova pipe incorporata per lo stesso nome di keyvaluepipe per aiutarti a scorrere attraverso oggetti, mappe e matrici, nel commonmodulo del pacchetto angolare. Per esempio -

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

Esempio di Fork Fork funzionante

controlla qui per ulteriori informazioni utili -

Se stai usando Angular v5 o inferiore o vuoi ottenere usando pipe segui questa risposta


1
lol Ho dovuto fare un aggiornamento ng6 solo per accedere a questa pipe - great stuff - thx
danday74

36
Puoi mantenere l'ordine delle chiavi originale usando un comparatore personalizzato: *ngFor="let item of testObject | keyvalue:keepOriginalOrder" e nella tua classe definire: public keepOriginalOrder = (a, b) => a.key
mike-shtil

2
public keepOriginalOrder = (a, b) => a.key thx molto per questo
Kumaresan Perumal,

1
questa dovrebbe essere la risposta - funziona bene su
Angular

1
Incredibile, questo non esisteva dalla prima versione
Carlos Pinzón,

227

È possibile creare una pipe personalizzata per restituire l'elenco di chiavi per ciascun elemento. Qualcosa del genere:

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

e usalo così:

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

modificare

È inoltre possibile restituire una voce contenente sia chiave che valore:

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

e usalo così:

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

1
notare la parentesi di chiusura mancante inkeys.push({key: key, value: value[key]);
Marton Pallagi

49
In realtà scoraggio chiunque dall'utilizzare le pipe per creare raccolte all'interno *ngFordell'espressione. Crea enormi colli di bottiglia delle prestazioni perché deve generare la raccolta ogni volta che il rilevatore di modifiche verifica le modifiche.
martin,

3
Grazie per la soluzione ... il problema è che ogni volta che l'oggetto cambia, la pipe non si aggiorna. Se aggiungo pure:falseal tubo, diventa molto inefficiente. Hai una soluzione per aggiornare manualmente la pipa ogni volta che cambio l'oggetto (rimuovi elemento)?
ncohen

4
La risposta è un po 'datata. La riga * ngFor = "# entry of content | keys" non funziona correttamente e il ciclo for ... in è meglio cambiare in "for (const key di Object.keys (value))"
Experimenter

2
@RachChen Non nei modelli: 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

Aggiornare

In 6.1.0-beta.1 è stato introdotto KeyValuePipe https://github.com/angular/angular/pull/24319

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

Esempio di Plunker

Versione precedente

Un altro approccio è quello di creare una NgForIndirettiva che verrà utilizzata come:

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

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

Da Angular 6.1 è possibile utilizzare la pipe del valore chiave :

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

Ma ha l'inconveniente che ordina l'elenco risultante in base al valore chiave. Se hai bisogno di qualcosa di neutrale:

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

Non dimenticare di specificare l' attributo pure: false pipe. In questo caso, la pipe viene invocata ad ogni ciclo di rilevamento delle modifiche, anche se il riferimento di input non è cambiato (come nel caso dell'aggiunta di proprietà a un oggetto).


Già condiviso la stessa risposta di cui sopra stackoverflow.com/a/51491848/5043867
Pardeep Jain

26

Elaborazione della risposta di @ Thierry con l'esempio.

Non esiste alcun pipe o metodo integrato da ottenere key and valuedal ciclo * ngFor. quindi dobbiamo creare pipe personalizzate per lo stesso. come ha detto Thierry, ecco la risposta con il codice.

** La classe pipe implementa il metodo di trasformazione dell'interfaccia PipeTransform che accetta un valore di input e un array opzionale di stringhe di parametri e restituisce il valore trasformato.

** Il metodo di trasformazione è essenziale per una pipe. L'interfaccia PipeTransform definisce tale metodo e guida sia gli strumenti che il compilatore. È facoltativo; Angular cerca ed esegue il metodo di trasformazione a prescindere. per maggiori informazioni riguardo pipe consultare qui

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

e la parte HTML è:

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

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

aggiornamento a RC

come suggerito dall'utente6123723 (grazie) nel commento ecco l'aggiornamento.

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

Questo deve essere aggiornato: ecco l'avvertimento che ottengo "#" all'interno delle espressioni è deprecato. Usa invece "let"! ("</li> -> <ul * ngIf =" demo "> <li [ERROR ->] * ngFor = '# chiave della demo | chiavi'> Chiave: {{key.key}}, valore: { {key.value}} </li> "): myComponent @ 56: 6
user6123723

Non sono sicuro che sia nuovo, ma per citare dai documenti:> Dobbiamo includere la nostra pipe nella matrice delle dichiarazioni di AppModule.
Akzidenzgrotesk,

18

@Marton ha sollevato un'obiezione importante alla risposta accettata in quanto il pipe crea una nuova raccolta su ogni rilevamento di modifica. Vorrei invece creare un HtmlService che fornisce una gamma di funzioni di utilità che la vista può utilizzare come segue:

@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
e come è meglio che semplicemente Object.keys(...)all'interno di * ngFor?
Simon_Weaver,

8
Perché sarà buttare: TypeError: Cannot read property 'keys' of undefined. Non sembra essere supportato nel modello.
Stephen Paul,

1
Funziona molto bene come soluzione ed evita i problemi di prestazioni indicati in precedenza. stackoverflow.com/questions/35534959/…
J. Adam Connor il

ciao, questa b può essere utilizzata non templatenell'opzione, ma nel codice html effettivo del modello? grazie
Scaramouche il

16

Se stai già utilizzando Lodash, puoi fare questo semplice approccio che include sia la chiave che il valore:

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

Nel file dattiloscritto, includere:

import * as _ from 'lodash';

e nel componente esportato, includere:

_: any = _;

scusate ma non c'è bisogno di usare una libreria extra come Lodash per queste cose. comunque i nuovi metodi sono sempre ben accetti :)
Pardeep Jain

8

Grazie per il tubo, ma ho dovuto apportare alcune modifiche prima di poterlo utilizzare in angolare 2 RC5. Modificata la riga di importazione Pipe e aggiunto anche il tipo di qualsiasi all'inizializzazione dell'array di chiavi.

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

sì, le importazioni sono state modificate
Pardeep Jain,

7

Nessuna delle risposte qui ha funzionato per me fuori dalla scatola, ecco cosa ha funzionato per me:

Crea pipes/keys.tscon i contenuti:

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

Aggiungi a app.module.ts(il tuo modulo principale):

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

e quindi aggiungi al tuo array di dichiarazioni del modulo qualcosa del genere:

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

Quindi nel tuo modello di visualizzazione puoi usare qualcosa del genere:

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

Ecco un buon riferimento che ho trovato se vuoi leggere di più.


posso sapere qual è la differenza tra la tua risposta e le altre risposte (usando solo la pipe) fornite sopra? sembra come sopra
Pardeep Jain

1
Sicuro 1. Gli esempi sopra usano * ngFor = "# entry" invece di * ngFor = "let entry of" e il mio compilatore non ha accettato la sintassi #entry, il riferimento non usa neanche #. "Let entry of (myData | keys)" sembra essere una soluzione migliore. 2. Neanche il mio compilatore ha convalidato la classe di tubi di esempio perché mancava tipi di dati espliciti, quindi l'ho aggiunto. 3. Gli esempi sopra non mostrano come integrare il Pipe in un progetto che fa la mia risposta, è necessario importarlo nel modulo principale.
Cjohansson,

haha sì offcourese, perché quando la risposta è stata data in quel momento sintassi compreso #ecc. tra l'altro anche la tua risposta è corretta senza dubbio
Pardeep Jain,

6

C'è una vera libreria che lo fa tra le altre belle pipe. Si chiama ngx-pipes .

Ad esempio, keys pipe restituisce le chiavi per un oggetto e valori pipe restituisce i valori per un oggetto:

tubo di chiavi

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

valori pipe

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

Non c'è bisogno di creare la tua pipa personalizzata :)


2
buona alternativa, ma la cosa è il motivo per cui utilizzare la libreria esterna per una semplice pace del codice se possiamo farlo usando un semplice pezzo di codice come pipe
Pardeep Jain,

2
Umm ... ma è una pipa? È solo una riga nel tuo package.json e altre due righe nel tuo modulo quando importi la libreria. D'altra parte, una pipe personalizzata necessita di un file separato con circa 10-20 righe di codice e anche le righe di importazione nel modulo. Troviamo molto facile usare ngx-pipe nei nostri progetti. Perché dovremmo reinventare la ruota? :)
RichieRock,

sì, senza dubbio, in realtà è un'opinione basata, puoi scegliere uno di questi due, nessuno è nel modo sbagliato.
Pardeep Jain,

2
Non dimenticare, se si scrive un tubo personalizzato, è necessario verificare che il tubo personalizzati pure . Quindi sono 10-20 righe di codice pipe e quindi probabilmente 20-40 righe di codice test per testare la pipe.
Danwellman,

4

Usa indice:

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

Uso:

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

1
Questo è qualcosa di nuovo per me, meglio se potessi aggiungere un esempio insieme alla tua risposta :) Puoi anche indicarmi qualsiasi documentazione per lo stesso?
Pardeep Jain,

Qual è il tipo di oggetti? Matrice o mappa? Per favore, chiariscilo. Grazie in anticipo
Basil Mohammed,

3

Ecco la soluzione semplice

Per questo puoi usare iteratori dattiloscritti

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

cambiare il tipo di demo in array o iterare sull'oggetto e passare a un altro array

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

e da HTML:

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

Questo non è un metodo appropriato, questo può essere fatto facilmente da chiunque.
Pardeep Jain,

1

Penso che Object.keys sia la migliore soluzione a questo problema. Per chiunque trovi questa risposta e stia cercando di scoprire perché Object.keys sta dando loro ['0', '1'] invece di ['key1', 'key2'], un avvertimento: fai attenzione alla differenza tra " di "e" in ":

Stavo già usando Object.keys, qualcosa di simile a questo:

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

Tuttavia, invece di

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

Avevo inavvertitamente scritto

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

che "ha funzionato" perfettamente bene senza alcun errore e restituito

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

Questo mi è costato circa 2 ore googling e imprecazioni ..

(schiaffeggia la fronte)


1

puoi ottenere la chiave dell'oggetto dinamico provandola

myObj['key']

0

Devi farlo così per ora, non lo so molto efficiente in quanto non vuoi convertire l'oggetto che ricevi dalla base di fuoco.

    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;

    });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.