ключ доступа и значение объекта, используя * ngFor

212

Бит путают о том, как получить Key and Value объекта в angular2, пока usng * ngFor для итерации над объектом. я знаю в angular 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. Как его получить? я искал много нашел идею использования трубы, но как использовать, я не знаю.  есть ли какая-либо inbuild труба для выполнения того же в angular2?

  • 2
    в настоящее время в angular2 ngFor нет поддержки key, value пары значений синтаксиса, вы должны посмотреть на этот ответ
  • 0
    @PankajParkar да, уже читал этот ответ. какой-нибудь альтернативный сейчас?
Показать ещё 7 комментариев
Теги:
angular
object

15 ответов

39
Лучший ответ

Как и в последнем выпуске Angular (v6.1.0), Angular Team добавила новую встроенную трубу для того же имени, что и keyvalue pipe, чтобы помочь вам перебирать объекты, карты и массивы в common модуле углового пакета. Например -

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

Пример с рабочей вилкой

ознакомьтесь с этой информацией для получения более полезной информации -

Если вы используете Angular v5 или ниже или хотите достичь использования трубы, следуйте этому ответу

  • 0
    @Dolan Я хотел бы принять два ответа, но на момент написания принятый ответ является правильным :)
  • 1
    LOL Я должен был сделать обновление NG6 только для доступа к этой трубе - отличная вещь - THX
Показать ещё 4 комментария
234

Доступно 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(){}
}
  • 1
    @PardeepJain - это метод ES5 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 21
    Это лучшее и более эффективное решение
Показать ещё 4 комментария
175

Вы можете создать настраиваемый канал, чтобы вернуть список ключей для каждого элемента. Что-то вроде этого:

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>

Edit

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

@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>
  • 0
    Что вы имеете в виду относительно асинхронности? Возможно, его использование в моем ответе неоднозначно. Трубе это не нужно. Вы можете использовать pipe, если объект, который вы хотите перебрать, является наблюдаемым ...
  • 0
    Спасибо! Кроме того, хорошее объяснение здесь, проверьте это - Как использовать трубы! medium.com/front-end-hacking/...
Показать ещё 10 комментариев
27

Обновить

В 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);
    }
  }
}
21

Разработка ответа @Thierry с примером.

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

** Класс pipe реализует метод преобразования интерфейса PipeTransform, который принимает входное значение и необязательный массив строк параметров и возвращает преобразованное значение.

** Метод преобразования необходим для трубы. Интерфейс PipeTransform определяет этот метод и направляет как инструменты, так и компилятор. Это необязательно; Angular ищет и выполняет метод преобразования независимо. для более подробной информации о канале см. здесь

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>
  • 0
    Это должно быть обновлено: вот предупреждение, которое я получаю "#" внутри выражений не рекомендуется. Используйте вместо этого «пусть»! ("</ li> -> <ul * ngIf =" demo "> <li [ERROR ->] * ngFor = '# ключ демонстрации | keys'> Key: {{key.key}}, значение: { {key.value}} </ li> "): myComponent @ 56: 6
  • 0
    Не уверен, что это новое, но процитировать из документации:> Мы должны включить наш канал в массив объявлений AppModule.
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?
  • 8
    Потому что он выдаст: TypeError: Cannot read property 'keys' of undefined . Кажется, он не поддерживается в шаблоне.
Показать ещё 1 комментарий
14

Если вы уже используете Lodash, вы можете сделать этот простой подход, который включает как ключ, так и значение:

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

В файле машинописных файлов:

import * as _ from 'lodash';

и в экспортированном компоненте:

_: any = _;
  • 0
    извините, но нет необходимости использовать дополнительные библиотеки, такие как Lodash для таких вещей. В любом случае всегда приветствуются новые методы :)
9

Из углового 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. В этом случае труба вызывается в каждом цикле обнаружения изменений, даже если исходная ссылка не изменилась (так бывает в случае добавления свойств к объекту).

7

Спасибо за трубку, но мне пришлось внести некоторые изменения, прежде чем я смогу использовать ее в angular 2 RC5. Изменена строка импорта труб, а также добавлен тип любого из них в инициализацию массива ключей.

 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;
  }
}
  • 0
    да импорт был изменен
6

Там действительно хорошая библиотека, которая делает это среди других приятных труб. Он называется ngx-pipes.

Например, клавиша pipe возвращает ключи для объекта, а значения pipe возвращают значения для объекта:

клавиша

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

значения pipe

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

Не нужно создавать свой собственный канал:)

  • 1
    хорошая альтернатива, но главное - использовать внешнюю библиотеку для простого кода, если мы можем сделать это, используя простой фрагмент кода, такой как pipe
  • 2
    Ммм ... но это труба? Это всего лишь одна строка в вашем package.json и еще две строки в вашем модуле при импорте библиотеки. С другой стороны, для пользовательского канала требуется отдельный файл с примерно 10-20 строками кода, а также строки импорта в вашем модуле. Мы находим использование ngx-pipe очень простым в наших проектах. Зачем нам изобретать велосипед? :)
Показать ещё 2 комментария
6

Ни один из ответов здесь не работал для меня из коробки, вот что сработало для меня:

Создайте 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>

Вот хорошая ссылка, которую я нашел, если вы хотите прочитать больше.

  • 0
    Могу ли я узнать, в чем разница между вашим ответом и другим ответом (используя только трубу), представленным выше? похоже, как и выше
  • 1
    Конечно 1. В приведенных выше примерах используется * ngFor = "# entry" вместо * ngFor = "let entry of", и мой компилятор не принял синтаксис #entry, ссылка также не использует #. "let entry of (myData | keys)" кажется лучшим решением. 2. Мой компилятор также не проверял пример класса трубы, потому что в нем отсутствовали явные типы данных, поэтому я добавил это. 3. В приведенных выше примерах не показано, как интегрировать Pipe в проект, как это делает мой ответ, вам нужно импортировать его в основной модуль.
Показать ещё 1 комментарий
3

Вот простое решение

Вы можете использовать итераторы typescript для этого

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

  • 0
    хороший как альтернатива :)
1

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

Я уже использовал 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 часов гуглинга и ругательства.

(похлопывает лоб)

0

изменить демо-тип на массив или выполнить итерацию по вашему объекту и нажать на другой массив

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>
  • 0
    Это не подходящий метод, это может легко сделать любой.
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;

    });

Ещё вопросы

Сообщество Overcoder
Наверх
Меню