ключ доступу та значення об’єкта за допомогою * ngFor


426

Я трохи заплутався про те , як отримати keyі valueоб'єкта в angular2 при використанні *ngForдля ітерація об'єкта. Я знаю, що в куті 1.x є такий синтаксис

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

але я не знаю, як це зробити в angular2. Я спробував щось подібне, без успіху:

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

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

Ось plnkr з моєю спробою: http://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

Як я можу отримати key1та key2динамічно користуватися *ngFor? Після широкого пошуку я знайшов ідею використовувати труби, але не знаю, як це зробити. Чи є якась вбудована труба для виконання того ж у angular2?


2
на даний момент немає key, valueсинтаксису підтримки парного типу у angular2 ngFor, слід поглянути на цю відповідь
Pankaj Parkar

@PankajParkar так, я вже прочитав цю відповідь. будь-який замінник наразі?
Pardeep Jain

@Pradeep Я не думаю про інший спосіб для цього, вам слід піти на створення власного Pipeдля цього ..
Pankaj Parkar

Хм, але я поняття не маю, як створити трубу для того ж.
Pardeep Jain

Відповідь @Pradeep, яку я дав вам для довідки, має таку реалізацію. вони повинні працювати ..
Панкай Паркер

Відповіді:


398

Майте Object.keysдоступ у шаблоні та використовуйте його в *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
Це краще та ефективніше рішення
Aous1000

1
@tomtastico Як би ви відобразили це для 3D-масиву? Наприклад {"1": {"1.1": ["1.1.1", "1.1.2"]}}. А потім вкладіть 3 ngFor's
Френк

@Frank ти щойно це сказав. Гніздо *ngForс. Перші два з використанням objectKeys, найпотаємнішого немає потреби (оскільки це просто масив).
tomtastico

1
Дивовижно. Налаштування objectKeys = Object.keys - це найпростіший метод, який я бачив, щоб можна було перевірити довжину об'єкта з HTML.
JAC

392

Як і в останньому випуску програми Angular (v6.1.0) , Angular Team додав нову вбудовану трубу для тієї ж назви, що і keyvaluepipe, щоб допомогти вам переглядати об'єкти, карти та масиви в commonмодулі кутового пакету. Наприклад -

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

Приклад роботи

ознайомтесь тут, щоб отримати більше корисної інформації -

Якщо ви використовуєте Angular v5 або нижче або хочете досягти використання труби, дотримуйтесь цієї відповіді


1
lol Мені довелося зробити оновлення ng6 просто для доступу до цієї труби - чудові речі - thx
понеділок74

36
Ви можете зберегти оригінальне замовлення ключа за допомогою спеціального компаратора: *ngFor="let item of testObject | keyvalue:keepOriginalOrder" а у своєму класі визначте: public keepOriginalOrder = (a, b) => a.key
mike-shtil

2
public holdOriginalOrder = (a, b) => a.key thx дуже багато для цього
Kumaresan Perumal

1
це повинна бути відповідь - добре працюючи на кутових 7
calios

1
Неймовірно, що цього не було з часів першої версії
Карлос Пінсон

227

Ви можете створити спеціальну трубу, щоб повернути список ключів для кожного елемента. Щось схоже:

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

і використовувати його так:

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

Редагувати

Ви також можете повернути запис, що містить і ключ, і значення:

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

і використовувати його так:

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

1
відзначте пропущену дужку закриття вkeys.push({key: key, value: value[key]);
Мартон Палладі

49
Я насправді нікого не заважаю використовувати труби для створення колекцій всередині *ngForвиразу. Це створює величезні вузькі місця, оскільки йому потрібно генерувати колекцію щоразу, коли детектор змін перевіряє на наявність змін.
март

3
Дякую за рішення ... проблема полягає в тому, що щоразу, коли об'єкт змінюється, труба не оновлюється. Якщо додати pure:falseдо труби, вона стає дуже неефективною. Чи є у вас рішення для оновлення труби вручну кожного разу, коли я зміню об'єкт (видалити елемент)?
ncohen

4
Відповідь трохи застаріла. Рядок * ngFor = "# запис вмісту | ключі" не працює належним чином, а цикл для ... у циклі краще змінити на "для (ключ const Object.keys (значення))"
Експериментатор

2
@RachChen Не в шаблонах: 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

Оновлення

У 6.1.0-beta.1 KeyValuePipe був представлений https://github.com/angular/angular/pull/24319

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

Приклад Плункер

Попередня версія

Інший підхід полягає у створенні NgForInдирективи, яка використовуватиметься так:

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

Приклад Плункер

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

З кутового 6.1 ви можете використовувати трубу ключового значення :

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

Але він незручно сортувати отриманий список за ключовим значенням. Якщо вам потрібно щось нейтральне:

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

Не забудьте вказати атрибут pure: false pipe. У цьому випадку труба викликається на кожному циклі виявлення змін, навіть якщо вхідне посилання не змінилося (так це відбувається, коли ви додаєте властивості до об'єкта).


Вже поділився тією ж відповіддю вище stackoverflow.com/a/51491848/5043867
Pardeep Jain

26

Розробка відповіді @ Тьєррі на прикладі.

Немає вбудованої труби чи методу для отримання key and valueз циклу * ngFor. тому ми повинні створити спеціальні труби для того ж. як сказав Тьєррі, тут відповідь з кодом.

** Клас труби реалізує метод перетворення інтерфейсу PipeTransform, який приймає вхідне значення та необов'язковий масив рядків параметрів та повертає перетворене значення.

** Метод перетворення є важливим для труби. Інтерфейс PipeTransform визначає цей метод та керує як інструментами, так і компілятором. Це необов’язково; Кутовий шукає і виконує метод перетворення незалежно. для отримання додаткової інформації про трубу дивіться тут

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

і частина HTML:

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

Робочий Plnkr http://plnkr.co/edit/50LlK0k6OnMnkc2kNHM2?p=preview

оновлення до RC

як запропонував користувач6123723 (спасибі) у коментарі тут є оновлення.

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

Це потрібно оновити. Ось попередження, що я отримую "#" всередині виразів, застаріле. Використовуйте натомість "нехай"! ("</li> -> <ul * ngIf =" demo "> <li [ПОМИЛКА ->] * ngFor = '# ключ демо-клавіш'> Ключ: {{key.key}}, значення: { {key.value}} </li> "): myComponent @ 56: 6
user6123723

Не впевнений, що це нове, але, щоб цитувати з документів:> Ми повинні включити нашу трубу в масив декларацій AppModule.
Akzidenzgrotesk

18

@Marton мав важливе заперечення проти прийнятої відповіді на тій підставі, що труба створює нову колекцію при кожному виявленні змін. Я б замість цього створив HtmlService, який надає низку корисних функцій, які перегляд може використовувати наступним чином:

@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
і як це краще, ніж просто Object.keys(...)всередині * ngFor?
Simon_Weaver

8
Тому що він буде кидати: TypeError: Cannot read property 'keys' of undefined. Схоже, це не підтримується в шаблоні.
Стівен Пол

1
Це дуже добре працює як рішення і дозволяє уникнути проблем, пов'язаних з виконанням, зазначеними вище. stackoverflow.com/questions/35534959/…
Дж. Адам Коннор

привіт, може це b використовується не в templateпараметрі, а в фактичному коді html шаблону? дякую
Scaramouche

16

Якщо ви вже використовуєте Lodash, ви можете скористатися цим простим підходом, який включає в себе і ключ, і значення:

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

У файл машинопису включайте:

import * as _ from 'lodash';

а в експортований компонент включають:

_: any = _;

Вибачте, але не потрібно використовувати для таких речей додаткову бібліотеку, як Лодаш. у будь-якому разі нові методи завжди вітаються :)
Pardeep Jain

8

Дякую за трубу, але мені довелося внести деякі зміни, перш ніж я зможу використовувати її в кутовій 2 RC5. Змінено імпортну лінію Pipe, а також додано тип будь-якого до ініціалізації масиву ключів.

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

так, імпорт був змінений
Pardeep Jain

7

Жодна з відповідей тут не працювала для мене поза скринькою, ось що для мене спрацювало:

Створити pipes/keys.tsіз вмістом:

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

Додати в app.module.ts(Ваш основний модуль):

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

а потім додайте до свого масиву декларацій модуля щось подібне:

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

Тоді у своєму шаблоні перегляду ви можете використовувати щось подібне:

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

Ось хороша довідка, яку я знайшов, якщо ви хочете прочитати більше.


чи можу я знати, яка різниця між вашою відповіддю та іншими відповідями (лише за допомогою труби), наведеними вище? здається так само, як вище
Pardeep Jain

1
Впевнено 1. Наведені вище приклади використовують * ngFor = "# запис" замість * ngFor = "Нехай запис", і мій компілятор не сприйняв синтаксис #entry, у посиланні також не використовується #. "нехай введення (myData | ключів)" здається кращим рішенням. 2. Мій компілятор не підтвердив приклад класу Pipe, тому що в ньому були відсутні явні типи даних, тому я додав це. 3. Наведені вище приклади не показують, як інтегрувати Трубу в проект, який робить моя відповідь, вам потрібно імпортувати його в основний модуль.
cjohansson

ха-ха-так, заперечуйте, адже коли відповідь була надана на той час синтаксисом, у тому числі #тощо. До речі, ваша відповідь також правильна, без сумніву,
Pardeep Jain,

6

Є справжня приємна бібліотека, яка робить це серед інших приємних труб. Це називається ngx-pipe .

Наприклад, клавіші pipe повертають ключі для об'єкта, а значення pipe повертають значення для об'єкта:

клавіші труби

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

значення труби

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

Не потрібно створювати власну власну трубу :)


2
добре чергуйте, але річ полягає в тому, щоб використовувати зовнішню бібліотеку для простого коду, якщо ми можемо це зробити за допомогою простого фрагмента коду, як pipe
Pardeep Jain

2
Гм ... але це труба? Це лише один рядок у пакеті.json та ще два рядки у вашому модулі, коли ви імпортуєте бібліотеку. З іншого боку, для спеціальної труби потрібен окремий файл з приблизно 10-20 рядками коду, а також рядки імпорту у вашому модулі. Ми вважаємо, що використовувати ngx-труби дуже просто в наших проектах. Навіщо нам винаходити колесо? :)
RichieRock

так, без сумніву, насправді її думка заснована, ви можете обирати між цими двома, ніхто не є помилковим способом.
Pardeep Jain

2
Не забувайте, що якщо ви пишете для користувача трубу, ви повинні перевірити , що призначені для користувача труби , а також . Це 10-20 рядків коду труби, а потім, мабуть, 20-40 рядків тестового коду для тестування труби.
danwellman

4

Використовуйте індекс:

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

Використання:

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

1
Це щось нове для мене, краще, якщо ви можете додати приклад разом зі своєю відповіддю :) Також чи можете ви вказати мені будь-яку документацію на те саме?
Pardeep Jain

Який тип об’єктів? Масив чи карта? Будь ласка, дайте зрозуміти. Заздалегідь дякую
Василь Мухаммед

3

Ось просте рішення

Для цього можна використовувати ітератори машинопису

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

змінити тип демонстрації на масив чи ітерацію над вашим об'єктом та перейти до іншого масиву

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

і з html:

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

Це не відповідний метод, це легко зробити будь-хто.
Pardeep Jain

1

Я думаю, що Object.keys - найкраще рішення цієї проблеми. Для тих, хто стикається з цією відповіддю і намагається з’ясувати, чому Object.keys дає їм ['0', '1'] замість ['key1', 'key2'], попереджувальну розповідь - остерігайтеся різниці між " з "і" в ":

Я вже використовував Object.keys, щось подібне до цього:

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

Однак замість

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

Я ненароком писав

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

який "спрацював" ідеально добре без жодної помилки і повернувся

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

Це коштувало мені близько 2 годин гугнути і проклинати ..

(ляпає чолом)


1

ви можете отримати ключ динамічного об'єкта, спробувавши це

myObj['key']

0

Ви повинні це зробити так, як зараз, я знаю, не дуже ефективно, оскільки ви не хочете конвертувати об'єкт, який ви отримуєте з 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;

    });
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.