Ключ доступа и значение объекта, используя *ngFor
Немного запутался о том, как получить Key and Value
объекта в angular2 при использовании *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. Как получить его? Я много искал, нашел идею использовать трубу, но как использовать, я не знаю. есть ли встроенная труба для того же в angular2?
22 ответа
Как и в последнем выпуске Angular (v6.1.0), Angular Team добавила новый встроенный канал для того же имени, что и keyvalue
трубу, чтобы помочь вам перебирать объекты, карты и массивы, в common
модуль угловой упаковки. Например -
<div *ngFor="let item of testObject | keyvalue">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
Пример работающего форка
проверьте это здесь для получения дополнительной полезной информации -
- https://github.com/angular/angular/blob/master/CHANGELOG.md#features-3
- https://github.com/angular/angular/commit/2b49bf7
Если вы используете Angular v5 или ниже или хотите добиться использования pipe, следуйте этому ответу
Есть 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(){}
}
Вы можете создать пользовательский канал для возврата списка ключей для каждого элемента. Что-то вроде того:
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>
Обновить
В 6.1.0-бета.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);
}
}
}
Из Angular 6.1 вы можете использовать keyvalue pipe:
<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. В этом случае канал вызывается при каждом цикле обнаружения изменений, даже если входная ссылка не изменилась (как в случае добавления свойств к объекту).
Разработка ответа @ Тьерри с примером.
Там нет встроенной трубы или метода, чтобы получить 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
как предложено user6123723(спасибо) в комментарии здесь обновление.
<ul>
<li *ngFor='let key of demo | keys'>
Key: {{key.key}}, value: {{key.value}}
</li>
</ul>
У @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...
}
Если вы уже используете Lodash, вы можете использовать этот простой подход, который включает в себя как ключ, так и значение:
<ul>
<li *ngFor='let key of _.keys(demo)'>{{key}}: {{demo[key]}}</li>
</ul>
В файле машинописи включите:
import * as _ from 'lodash';
и в экспортируемый компонент, включают:
_: any = _;
Подумал добавить ответ для Angular 8:
Для зацикливания вы можете:
<ng-container *ngFor="let item of BATCH_FILE_HEADERS | keyvalue: keepOriginalOrder">
<th nxHeaderCell>{{'upload.bulk.headings.'+item.key |translate}}</th>
</ng-container>
Также, если вам нужен указанный выше массив для сохранения исходного порядка, объявите это внутри своего класса:
public keepOriginalOrder = (a, b) => a.key;
Спасибо за трубу, но мне пришлось внести некоторые изменения, прежде чем я смог использовать ее в угловых 2 RC5. Изменена строка импорта Pipe, а также добавлен тип any в инициализацию массива ключей.
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;
}
}
You can use the
keyvalue
pipe as the sample code is provided:
<div style="flex-direction: column">
<app-cart-item
class="cart-item"
*ngFor="let keyValuePair of this.allProductRecords | keyvalue"
[productRecord]="keyValuePair.value"
(removeProduct)="removeProductFromCart(keyValuePair.key)"
></app-cart-item>
<br />
<p style="font-family: Verdana, Geneva, Tahoma, sans-serif; font-weight: bolder">
Total ${{ getTotalPurchaseAmount() }}
</p>
</div>
Используйте индекс:
<div *ngFor="let value of Objects; index as key">
Использование:
{{key}} -> {{value}}
Ни один из ответов здесь не сработал для меня из коробки, вот что сработало для меня:
Создайте 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>
Вот хорошая ссылка, которую я нашел, если вы хотите узнать больше.
Есть действительно хорошая библиотека, которая делает это среди других хороших каналов. Это называется ngx-pipe.
Например, pipe pipe возвращает ключи для объекта, а pipe values возвращает значения для объекта:
ключи трубы
<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 -->
Не нужно создавать свои собственные трубы:)
Решение ECMA 8+
Следуя идее некоторых других ответов, вы можете создать Pipe для создания массива из вашего объекта, но гораздо проще.
Трубка
import { PipeTransform, Pipe } from '@angular/core';
/**
* Transform a literal object into array
*/
@Pipe({
name: 'forObject',
pure: true,
})
export class ForObjectPipe implements PipeTransform {
transform(object, args?: any): any {
return Object.values(object);
}
}
Модуль
В своем модуле вы объявляете и предоставляете его
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ForObjectPipe } from './for-object.pipe';
import { MyPageRoutingModule } from './my-routing.module';
import { MyPage } from './my.page';
@NgModule({
imports: [
CommonModule,
MyPageRoutingModule,
],
declarations: [
MyPage,
ForObjectPipe,
],
providers: [
ForObjectPipe,
]
})
export class MyPageModule {}
Затем вы можете использовать в своем компоненте машинописный код или HTML.
Использование в компоненте
// ...
import { ForObjectPipe } from './for-object.pipe';
@Component({
selector: 'app-my',
templateUrl: './my.page.html',
styleUrls: ['./my.page.scss'],
})
export class MyComponent {
obj: { [key: any]: any } = {
1: 'hello',
2: 'word',
};
constructor(private forObjectPipe: ForObjectPipe) { }
foo() {
const myArray = this.forObjectPipe.transform(this.obj);
// same as
const myArray = Object.values(this.obj);
}
}
Использование в представлении компонента
<p>Object values:</p>
<div *ngFor="let value of obj | forObject">
{{ value }}
</div>
Выход:
Object values:
hello
world
Вот простое решение
Вы можете использовать итераторы машинописи для этого
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]
}
}
}
};
}
}
Вы можете получить ключ динамического объекта с помощью этого
myObj['key']
Измените демонстрационный тип на массив или переберите свой объект и нажмите на другой массив
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>
Я думаю, что 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 часов, гуглил и ругался..
(шлепает по лбу)
Вы должны сделать это сейчас, я знаю, что это не очень эффективно, так как вы не хотите конвертировать объект, который вы получаете от 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;
});
использовать фильтр keyValue
- item.key даст ключ объекта
- item.value даст значение объекта
<div *ngFor="let item of object | keyvalue">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
Создайте свой массив следующим образом
tags = [
{
name : 'Aliko Dogara',
amount : '60,000',
purpose: 'Office repairs'
},
{
name : 'Aliko Dogara',
amount : '60,000',
purpose: 'Office repairs'
},
{
name : 'Aliko Dogara',
amount : '60,000',
purpose: 'Office repairs'
},
{
name : 'Aliko Dogara',
amount : '60,000',
purpose: 'Office repairs'
},
{
name : 'Aliko Dogara',
amount : '60,000',
purpose: 'Office repairs'
}
];
работает все время