clé d'accès et valeur de l'objet à l'aide de * ngFor


426

Je suis un peu confus sur la façon d'obtenir le keyet valued'un objet dans angular2 tout en l'utilisant *ngForpour itérer sur l'objet. Je sais que dans 1.x angulaire, il y a une syntaxe comme

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

mais je ne sais pas faire la même chose en angular2. J'ai essayé quelque chose de similaire, sans succès:

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

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

Voici un plnkr avec ma tentative: http://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

Comment puis-je obtenir key1et key2utiliser dynamiquement *ngFor? Après de longues recherches, j'ai trouvé l'idée d'utiliser des pipes mais je ne sais pas comment m'y prendre. Y a-t-il un tuyau intégré pour faire la même chose dans angular2?


2
actuellement, il n'y a pas de key, valuetype de syntaxe de paire de support dans angular2 ngFor, vous devriez regarder cette réponse
Pankaj Parkar

@PankajParkar ouais déjà lu cette réponse. un remplaçant pour l'instant?
Pardeep Jain

@Pradeep Je ne pense à aucun autre moyen pour cela maintenant, vous devriez opter pour la création de votre propre compte Pipepour cela ..
Pankaj Parkar

hmm mais je ne sais pas comment créer un tuyau pour le même.
Pardeep Jain

La réponse @Pradeep que je vous ai donnée pour référence, a cette implémentation. ils devraient travailler ..
Pankaj Parkar

Réponses:


400

Avoir Object.keysaccessible dans le modèle et l'utiliser dans *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
C'est une solution meilleure et plus efficace
Aous1000

1
@tomtastico Comment afficheriez-vous cela pour une matrice 3D? Par exemple {"1": {"1.1": ["1.1.1", "1.1.2"]}}. Et puis imbriquer 3 ngFor
Frank

@Frank vous venez de le dire vous-même. Nid le *ngFors. Les deux premiers à utiliser objectKeys, la plupart du temps pas besoin (car c'est juste un tableau).
tomtastico

1
Impressionnant. Définir objectKeys = Object.keys est la méthode la plus simple que j'ai vue pour pouvoir vérifier la longueur d'un objet à partir du code HTML.
JAC

394

Comme dans la dernière version d'Angular (v6.1.0) , Angular Team a ajouté un nouveau tuyau intégré pour le même nom que keyvaluetuyau pour vous aider à parcourir les objets, les cartes et les tableaux, dans le commonmodule du paquet angulaire. Par exemple -

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

Exemple de travail fourchu

consultez-le ici pour plus d'informations utiles -

Si vous utilisez Angular v5 ou une version antérieure ou si vous souhaitez utiliser un tuyau, suivez cette réponse


1
lol j'ai dû faire une mise à jour ng6 juste pour accéder à cette pipe - super trucs - thx
danday74

36
Vous pouvez conserver l'ordre des clés d'origine à l'aide d'un comparateur personnalisé: *ngFor="let item of testObject | keyvalue:keepOriginalOrder" et dans votre classe définir: public keepOriginalOrder = (a, b) => a.key
mike-shtil

2
public keepOriginalOrder = (a, b) => a.key thx beaucoup pour cela
Kumaresan Perumal

1
cela devrait être la réponse - fonctionne bien sur angulaire 7
calios

1
Incroyable ce n'était pas là depuis la première version
Carlos Pinzón

227

Vous pouvez créer un canal personnalisé pour renvoyer la liste des clés pour chaque élément. Quelque chose comme ca:

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

et l'utiliser comme ça:

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

Éditer

Vous pouvez également renvoyer une entrée contenant à la fois la clé et la valeur:

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

et l'utiliser comme ça:

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

1
noter le support de fermeture manquantkeys.push({key: key, value: value[key]);
Marton Pallagi

49
En fait, je décourage quiconque d'utiliser des tuyaux pour créer des collections dans l' *ngForexpression. Il crée un goulot d'étranglement énorme en termes de performances car il doit générer la collection à chaque fois que le détecteur de changement vérifie les changements.
martin

3
Merci pour la solution ... le problème est que chaque fois que l'objet change, le tube ne se met pas à jour. Si j'ajoute pure:falseau tuyau, cela devient très inefficace. Avez-vous une solution pour mettre à jour le tuyau manuellement chaque fois que je change d'objet (supprimer un élément)?
ncohen

4
La réponse est un peu dépassée. La ligne * ngFor = "# entrée de contenu | clés" ne fonctionne pas correctement et la boucle for ... in est préférable de passer à "for (const key of Object.keys (value))"
Experimenter

2
@RachChen Pas dans les modèles: 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

Mise à jour

Dans 6.1.0-beta.1 KeyValuePipe a été introduit https://github.com/angular/angular/pull/24319

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

Exemple Plunker

La version précédente

Une autre approche consiste à créer une NgForIndirective qui sera utilisée comme:

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

Exemple 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

Depuis Angular 6.1, vous pouvez utiliser le canal de valeur - clé :

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

Mais il présente l'inconvénient de trier la liste résultante par valeur de clé. Si vous avez besoin de quelque chose de neutre:

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

N'oubliez pas de spécifier l' attribut pure: false pipe. Dans ce cas, le canal est invoqué à chaque cycle de détection de changement, même si la référence d'entrée n'a pas changé (c'est le cas lorsque vous ajoutez des propriétés à un objet).


Déjà partagé la même réponse ci-dessus stackoverflow.com/a/51491848/5043867
Pardeep Jain

26

Elaboration de la réponse de @ Thierry avec exemple.

Il n'y a ni tuyau ni méthode intégrés pour obtenir key and value de la boucle * ngFor. nous devons donc créer un tuyau personnalisé pour le même. comme l'a dit thierry, voici la réponse avec le code.

** La classe pipe implémente la méthode de transformation de l'interface PipeTransform qui prend une valeur d'entrée et un tableau facultatif de chaînes de paramètres et renvoie la valeur transformée.

** La méthode de transformation est essentielle à un tuyau. L'interface PipeTransform définit cette méthode et guide à la fois l'outillage et le compilateur. C'est facultatif; Angular recherche et exécute la méthode de transformation indépendamment. pour plus d'informations concernant les tuyaux, reportez-vous ici

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

et la partie HTML est:

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

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

mise à jour vers RC

comme suggéré par user6123723 (merci) en commentaire voici la mise à jour.

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

Cela doit être mis à jour: voici l'avertissement que je reçois "#" à l'intérieur des expressions est déconseillé. Utilisez plutôt "let"! ("</li> -> <ul * ngIf =" demo "> <li [ERROR ->] * ngFor = '# key of demo | keys'> Key: {{key.key}}, value: { {key.value}} </li> "): myComponent @ 56: 6
user6123723

Je ne sais pas si c'est nouveau, mais pour citer les documents:> Nous devons inclure notre pipe dans le tableau des déclarations de l'AppModule.
Akzidenzgrotesk

18

@Marton avait une objection importante à la réponse acceptée au motif que le tuyau crée une nouvelle collection à chaque détection de changement. Je voudrais plutôt créer un HtmlService qui fournit une gamme de fonctions utilitaires que la vue peut utiliser comme suit:

@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
et comment est-ce mieux que juste Object.keys(...)à l'intérieur du * ngFor?
Simon_Weaver

8
Parce qu'il va jeter: TypeError: Cannot read property 'keys' of undefined. Il ne semble pas être pris en charge dans le modèle.
Stephen Paul

1
Cela fonctionne très bien comme solution et évite les problèmes de performances mentionnés ci-dessus. stackoverflow.com/questions/35534959/…
J. Adam Connor

bonjour, ce b peut-il pas être utilisé dans l' templateoption, mais dans le code html réel du modèle? merci
Scaramouche

16

Si vous utilisez déjà Lodash, vous pouvez faire cette approche simple qui comprend à la fois la clé et la valeur:

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

Dans le fichier dactylographié, incluez:

import * as _ from 'lodash';

et dans le composant exporté, incluez:

_: any = _;

désolé mais pas besoin d'utiliser une bibliothèque supplémentaire comme Lodash pour de telles choses. de toute façon les nouvelles méthodes sont toujours les bienvenues :)
Pardeep Jain

8

Merci pour le tuyau mais j'ai dû faire quelques changements avant de pouvoir l'utiliser en angulaire 2 RC5. Modification de la ligne d'importation de tuyaux et ajout d'un type quelconque à l'initialisation du tableau de clés.

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

ouais les importations ont été modifiées
Pardeep Jain

7

Aucune des réponses ici n'a fonctionné pour moi hors de la boîte, voici ce qui a fonctionné pour moi:

Créer pipes/keys.tsavec du contenu:

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

Ajouter à app.module.ts(votre module principal):

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

puis ajoutez à votre tableau de déclarations de module quelque chose comme ceci:

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

Ensuite, dans votre modèle de vue, vous pouvez utiliser quelque chose comme ceci:

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

Voici une bonne référence que j'ai trouvée si vous voulez en savoir plus.


puis-je savoir quelle est la différence entre votre réponse et les autres réponses (en utilisant uniquement la pipe) fournies ci-dessus? il semble le même que ci
Pardeep Jain

1
Bien sûr 1. Les exemples ci-dessus utilisent * ngFor = "# entry" au lieu de * ngFor = "let entry of" et mon compilateur n'a pas accepté la syntaxe #entry, la référence n'utilise pas # non plus. "laisser l'entrée de (myData | keys)" semble être une meilleure solution. 2. Mon compilateur n'a pas non plus validé l'exemple de classe de tuyau car il manquait des types de données explicites, j'ai donc ajouté cela. 3. Les exemples ci-dessus ne montrent pas comment intégrer le Pipe dans un projet, ce que ma réponse fait, vous devez l'importer dans le module principal.
cjohansson

haha oui offcourese, parce que quand la réponse a été donnée à ce moment-là, la syntaxe y compris #etc. btw votre réponse est également correcte sans aucun doute
Pardeep Jain

6

Il y a une vraie belle bibliothèque qui fait ça parmi d'autres belles pipes. Cela s'appelle ngx-pipes .

Par exemple, le canal de clés renvoie les clés d'un objet et le canal de valeurs renvoie les valeurs d'un objet:

tuyau de clés

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

tuyau de valeurs

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

Pas besoin de créer votre propre pipe sur mesure :)


2
bonne alternative, mais la chose est pourquoi utiliser une bibliothèque externe pour une paix de code simple si nous pouvons le faire en utilisant un morceau de code simple comme un tuyau
Pardeep Jain

2
Umm ... mais c'est une pipe? C'est juste une ligne dans votre package.json et deux autres lignes dans votre module lorsque vous importez la bibliothèque. D'un autre côté, un canal personnalisé a besoin d'un fichier séparé avec 10 à 20 lignes de code et également les lignes d'importation dans votre module. Nous trouvons l'utilisation de ngx-pipes très facile dans nos projets. Pourquoi réinventer la roue? :)
RichieRock

oui sans aucun doute, en fait c'est une opinion, vous pouvez choisir entre ces deux, personne ne se trompe.
Pardeep Jain

2
Ne pas oublier, si vous écrivez un tuyau personnalisé, vous devez vérifier que tuyau personnalisé ainsi . Il s'agit donc de 10 à 20 lignes de code de tuyau, puis probablement de 20 à 40 lignes de code de test pour tester le tuyau.
danwellman

4

Utiliser l'index:

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

Usage:

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

1
C'est quelque chose de nouveau pour moi, mieux si vous pouviez ajouter un exemple avec votre réponse :) Pouvez-vous également me diriger vers une documentation pour la même chose?
Pardeep Jain

Quel est le type d'objets? Tableau ou carte? Veuillez préciser. Merci d'avance
Basil Mohammed

3

Voici la solution simple

Vous pouvez utiliser des itérateurs dactylographiés pour cela

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

changer le type de démonstration en tableau ou itérer sur votre objet et pousser vers un autre tableau

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

et de html:

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

Ce n'est pas une méthode appropriée, cela peut être facilement fait par n'importe qui.
Pardeep Jain

1

Je pense que Object.keys est la meilleure solution à ce problème. Pour tous ceux qui rencontrent cette réponse et essaient de savoir pourquoi Object.keys leur donne ['0', '1'] au lieu de ['key1', 'key2'], un récit édifiant - attention à la différence entre " de "et" dans ":

J'utilisais déjà Object.keys, quelque chose de similaire à ceci:

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

Cependant, au lieu de

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

J'avais écrit par inadvertance

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

qui "a fonctionné" parfaitement bien sans aucune erreur et est retourné

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

Cela m'a coûté environ 2 heures sur Google et maudire ..

(gifle le front)


1

vous pouvez obtenir la clé de l'objet dynamique en essayant ceci

myObj['key']

0

Vous devez le faire comme ça pour l'instant, je sais pas très efficace car vous ne voulez pas convertir l'objet que vous recevez de 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;

    });
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.