khóa truy cập và giá trị của đối tượng bằng cách sử dụng * ngFor


425

Tôi hơi bối rối về cách lấy keyvaluecủa một đối tượng trong angular2 trong khi sử dụng *ngForđể lặp lại đối tượng. Tôi biết trong góc 1.x có một cú pháp như

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

nhưng tôi không biết làm thế nào để làm điều tương tự trong angular2. Tôi đã thử một cái gì đó tương tự, nhưng không thành công:

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

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

Đây là một plnkr với nỗ lực của tôi: http://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

Làm thế nào tôi có thể nhận key1key2sử dụng động *ngFor? Sau khi tìm kiếm rộng rãi, tôi tìm thấy ý tưởng sử dụng đường ống nhưng tôi không biết làm thế nào để đi về nó. Có bất kỳ đường ống sẵn có để làm tương tự trong angular2?


2
hiện tại không có key, valueloại cú pháp hỗ trợ nào trong angular2 ngFor, bạn nên xem câu trả lời này
Pankaj Parkar

@PankajParkar yeah đã đọc câu trả lời này. bất kỳ thay thế cho bây giờ?
Pardeep Jain

@Pradeep Tôi không nghĩ ra bất kỳ cách nào khác cho việc này bây giờ, bạn nên đi tạo riêng Pipecho việc này ..
Pankaj Parkar

hmm nhưng tôi không có ý tưởng làm thế nào để tạo ra đường ống cho cùng.
Pardeep Jain

@Pradeep trả lời mà tôi đã cho bạn tham khảo, có triển khai đó. họ nên làm việc ..
Pankaj Parkar

Câu trả lời:


398

Object.keysthể truy cập trong mẫu và sử dụng nó trong *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
Đây là một giải pháp tốt hơn và hiệu quả hơn
Aous1000

1
@tomtastico Làm thế nào bạn sẽ hiển thị điều này cho một mảng 3D? Ví dụ: {"1": {"1.1": ["1.1.1", "1.1.2"]}}. Và sau đó lồng 3 ngFor
Frank

@Frank bạn chỉ cần tự nói. Tổ yến *ngFors. Hai lần đầu tiên sử dụng objectKeys, trong cùng không cần (vì nó chỉ là một mảng).
tomtastico

1
Tuyệt vời. Đặt objectKeys = Object.keys là phương pháp đơn giản nhất tôi từng thấy để có thể kiểm tra độ dài của một đối tượng từ HTML.
JAC

391

Như trong bản phát hành mới nhất của Angular (v6.1.0) , Angular Team đã thêm ống mới được xây dựng cho cùng tên với keyvalueống để giúp bạn lặp qua các đối tượng, bản đồ và mảng, trong commonmô-đun của gói góc. Ví dụ -

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

Ví dụ làm việc

kiểm tra nó ở đây để biết thêm thông tin hữu ích -

Nếu bạn đang sử dụng Angular v5 trở xuống hoặc bạn muốn đạt được bằng cách sử dụng đường ống, hãy làm theo câu trả lời này


1
lol Tôi đã phải thực hiện cập nhật ng6 chỉ để truy cập vào đường ống này - công cụ tuyệt vời - thx
danday74

36
Bạn có thể giữ thứ tự khóa gốc bằng bộ so sánh tùy chỉnh: *ngFor="let item of testObject | keyvalue:keepOriginalOrder" và trong lớp của bạn xác định: public keepOriginalOrder = (a, b) => a.key
mike-shtil

2
công khai giữ nguyên bản gốc = (a, b) => a.key thx rất nhiều cho việc này
Kumaresan Perumal

1
đây sẽ là câu trả lời - hoạt động tốt trên góc 7
calios

1
Không thể tin được điều này đã không xuất hiện kể từ phiên bản đầu tiên
Carlos Pinzón

227

Bạn có thể tạo một đường ống tùy chỉnh để trả về danh sách khóa cho từng thành phần. Một cái gì đó như thế:

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

và sử dụng nó như thế:

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

Biên tập

Bạn cũng có thể trả về một mục chứa cả khóa và giá 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;
  }
}

và sử dụng nó như thế:

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

1
lưu ý khung đóng bị thiếu trongkeys.push({key: key, value: value[key]);
Marton Pallagi

49
Tôi thực sự không khuyến khích bất cứ ai sử dụng các đường ống để tạo ra các bộ sưu tập bên trong *ngForbiểu thức. Nó tạo ra nút cổ chai hiệu năng lớn vì nó cần tạo bộ sưu tập mỗi khi bộ phát hiện thay đổi kiểm tra các thay đổi.
martin

3
Cảm ơn giải pháp ... vấn đề là bất cứ khi nào đối tượng thay đổi, đường ống không cập nhật. Nếu tôi thêm pure:falsevào đường ống, nó trở nên rất kém hiệu quả. Bạn có một giải pháp để cập nhật đường ống theo cách thủ công bất cứ khi nào tôi thay đổi đối tượng (xóa mục) không?
ncohen

4
Câu trả lời là một chút lỗi thời. Dòng * ngFor = "# entry of content | key" không hoạt động chính xác và vòng lặp for ... in tốt hơn để thay đổi thành "for (const const của Object.keys (value))"
Experimenter

2
@RachChen Không có trong các mẫu: 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

Cập nhật

Trong 6.1.0-beta.1 KeyValuePipe đã được giới thiệu https://github.com/angular/angular/pull/24319

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

Ví dụ Plunker

Phiên bản trước

Một cách tiếp cận khác là tạo ra lệnh NgForInsẽ được sử dụng như:

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

Ví dụ 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

Từ Angular 6.1, bạn có thể sử dụng ống keyvalue :

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

Nhưng nó có sự bất tiện khi sắp xếp danh sách kết quả theo giá trị khóa. Nếu bạn cần một cái gì đó trung lập:

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

Đừng quên chỉ định thuộc tính ống thuần: false . Trong trường hợp này, đường ống được gọi trên mỗi chu kỳ phát hiện thay đổi, ngay cả khi tham chiếu đầu vào không thay đổi (đó là trường hợp khi bạn thêm thuộc tính vào một đối tượng).


Đã chia sẻ cùng một câu trả lời ở trên stackoverflow.com/a/51491848/5043867
Pardeep Jain

26

Xây dựng câu trả lời của @ Thierry với ví dụ.

Không có đường ống hoặc phương thức sẵn có để lấy key and valuetừ vòng lặp * ngFor. vì vậy chúng ta phải tạo đường ống tùy chỉnh cho cùng. như thierry đã nói ở đây là câu trả lời với mã.

** Lớp ống thực hiện phương thức biến đổi của giao diện TubeTransform lấy giá trị đầu vào và một mảng tùy chọn của các chuỗi tham số và trả về giá trị được chuyển đổi.

** Phương pháp biến đổi là điều cần thiết cho một đường ống. Giao diện TubeTransform định nghĩa phương thức đó và hướng dẫn cả công cụ và trình biên dịch. Nó là tùy chọn; Angular tìm kiếm và thực hiện phương thức biến đổi bất kể. để biết thêm thông tin liên quan đến đường ống tham khảo ở đây

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

và phần HTML là:

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

Làm việc Plnkr http://plnkr.co/edit/50LlK0k6OnMnkc2kNHM2?p=preview

cập nhật lên RC

như được đề xuất bởi user6123723 (cảm ơn) trong bình luận ở đây là cập nhật.

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

Điều này cần phải được cập nhật: Đây là cảnh báo tôi nhận được "#" bên trong các biểu thức không được chấp nhận. Sử dụng "cho phép" thay vì! ("</ li> -> <ul * ng If =" demo "> <li [ERROR ->] * ngFor = '# key của demo | key'> Key: {{key.key}}, value: { {key.value}} </ li> "): myComponent @ 56: 6
user6123723

Không chắc chắn nếu điều này là mới, nhưng để trích dẫn từ các tài liệu:> Chúng ta phải đưa đường ống của mình vào mảng khai báo của AppModule.
Akzidenzgrotesk

18

@Marton đã phản đối quan trọng đối với câu trả lời được chấp nhận với lý do đường ống tạo ra một bộ sưu tập mới trên mỗi phát hiện thay đổi. Thay vào đó, tôi sẽ tạo một HtmlService cung cấp một loạt các chức năng tiện ích mà chế độ xem có thể sử dụng như sau:

@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
và làm thế nào là tốt hơn so với chỉ Object.keys(...)bên trong * ngFor?
Simon_Weaver

8
Bởi vì nó sẽ ném : TypeError: Cannot read property 'keys' of undefined. Nó dường như không được hỗ trợ trong mẫu.
Stephen Paul

1
Điều này hoạt động rất tốt như là một giải pháp và tránh các vấn đề hiệu suất được chỉ ra ở trên. stackoverflow.com/questions/35534959/ hy
J. Adam Connor

xin chào, b này có thể được sử dụng không trong templatetùy chọn, nhưng trong mã html thực tế của mẫu không? cảm ơn
Scaramouche

16

Nếu bạn đã sử dụng Lodash, bạn có thể thực hiện phương pháp đơn giản này bao gồm cả khóa và giá trị:

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

Trong tệp bản thảo, bao gồm:

import * as _ from 'lodash';

và trong thành phần xuất khẩu, bao gồm:

_: any = _;

xin lỗi nhưng không cần sử dụng thêm thư viện như Lodash cho những thứ như vậy. Dù sao phương pháp mới luôn được chào đón :)
Pardeep Jain 7/2/2017

8

Cảm ơn về đường ống nhưng tôi đã phải thực hiện một số thay đổi trước khi tôi có thể sử dụng nó trong góc 2 RC5. Đã thay đổi dòng nhập ống và cũng thêm loại bất kỳ vào khởi tạo mảng khóa.

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

yeah nhập khẩu đã được thay đổi
Pardeep Jain

7

Không có câu trả lời nào ở đây có tác dụng với tôi, đây là câu trả lời cho tôi:

Tạo pipes/keys.tsvới nội dung:

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

Thêm vào app.module.ts(mô-đun chính của bạn):

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

và sau đó thêm vào mảng khai báo mô-đun của bạn một cái gì đó như thế này:

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

Sau đó, trong mẫu xem của bạn, bạn có thể sử dụng một cái gì đó như thế này:

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

Đây là một tài liệu tham khảo tốt tôi tìm thấy nếu bạn muốn đọc thêm.


Tôi có thể biết sự khác biệt giữa các câu trả lời của bạn và câu trả lời khác (chỉ sử dụng đường ống) được cung cấp ở trên không? nó có vẻ giống như trên
Pardeep Jain

1
Chắc chắn 1. Các ví dụ trên sử dụng * ngFor = "# entry" thay vì * ngFor = "let entry of" và trình biên dịch của tôi không chấp nhận cú pháp #entry, tham chiếu cũng không sử dụng #. "cho phép nhập (myData | phím)" dường như là một giải pháp tốt hơn. 2. Trình biên dịch của tôi không xác nhận lớp ống mẫu vì nó thiếu các kiểu dữ liệu rõ ràng nên tôi đã thêm nó. 3. Các ví dụ trên không chỉ ra cách tích hợp ống vào dự án mà câu trả lời của tôi làm, bạn cần nhập nó vào mô-đun chính.
cjohansson

haha vâng, vì khi câu trả lời được đưa ra vào thời điểm đó bao gồm cả #v.v ... btw câu trả lời của bạn cũng không còn nghi ngờ gì nữa
Pardeep Jain

6

Có một thư viện đẹp thực sự làm điều này trong số các đường ống đẹp khác. Nó được gọi là ngx-pipe .

Ví dụ, đường ống khóa trả về khóa cho một đối tượng và đường ống giá trị trả về giá trị cho đối tượng:

chìa khóa ống

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

ống giá trị

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

Không cần phải tạo đường ống tùy chỉnh của riêng bạn :)


2
tốt thay thế, nhưng điều là tại sao nên sử dụng thư viện bên ngoài để có được mã đơn giản nếu chúng ta có thể làm điều này bằng cách sử dụng đoạn mã đơn giản như ống
Pardeep Jain

2
Umm ... nhưng nó là một đường ống? Đó chỉ là một dòng trong gói.json và hai dòng khác trong mô-đun của bạn khi bạn nhập thư viện. Mặt khác, một ống tùy chỉnh cần một tệp riêng với khoảng 10-20 dòng mã và cả các dòng nhập trong mô-đun của bạn. Chúng tôi thấy việc sử dụng ngx-pipe rất dễ dàng trong các dự án của chúng tôi. Tại sao chúng ta nên phát minh lại bánh xe? :)
RichieRock

Vâng, không có nghi ngờ gì, thực sự đó là một ý kiến ​​dựa trên, bạn có thể chọn một trong hai điều này, không ai sai cả.
Pardeep Jain

2
Đừng quên, nếu bạn viết một ống tùy chỉnh, bạn phải kiểm tra rằng ống tùy chỉnh cũng . Vì vậy, đó là 10-20 dòng mã ống, và sau đó có thể là 20-40 dòng mã kiểm tra để kiểm tra đường ống.
danwellman

4

Sử dụng chỉ mục:

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

Sử dụng:

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

1
Đó là một cái gì đó mới đối với tôi, Tốt hơn Nếu bạn có thể thêm ví dụ cùng với câu trả lời của bạn :) Ngoài ra bạn có thể chỉ cho tôi bất kỳ tài liệu nào cho cùng không?
Pardeep Jain

Các loại đối tượng là gì? Mảng hay Bản đồ? Hãy làm cho nó rõ ràng. Cảm ơn trước
Basil Mohammed

3

Đây là giải pháp đơn giản

Bạn có thể sử dụng các trình vòng lặp typcript cho việc này

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

thay đổi loại demo thành mảng hoặc lặp qua đối tượng của bạn và đẩy sang mảng khác

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

và từ html:

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

Đây không phải là một phương pháp thích hợp, điều này có thể dễ dàng thực hiện bởi bất cứ ai.
Pardeep Jain 17/07/18

1

Tôi nghĩ Object.keys là giải pháp tốt nhất cho vấn đề này. Đối với bất kỳ ai bắt gặp câu trả lời này và đang cố gắng tìm hiểu tại sao Object.keys lại cho họ ['0', '1'] thay vì ['key1', 'key2'], một câu chuyện cảnh báo - hãy coi chừng sự khác biệt giữa " của "và" trong ":

Tôi đã sử dụng Object.keys, một cái gì đó tương tự như thế này:

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

Tuy nhiên, thay vì

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

Tôi đã vô tình viết

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

Mà "làm việc" hoàn toàn tốt mà không có lỗi và trả lại

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

Điều đó làm tôi mất khoảng 2 giờ để googling và nguyền rủa ..

(tát trán)


1

bạn có thể lấy khóa của đối tượng động bằng cách thử

myObj['key']

0

Bây giờ bạn phải làm như thế này, tôi biết không hiệu quả lắm vì bạn không muốn chuyển đổi đối tượng bạn nhận được từ căn cứ hỏa lực.

    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;

    });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.