尝试 *ngFor Json 对象时出错

Error trying to *ngFor a Json object

我正在尝试创建一个从端点返回的数据列表,我得到了 10 位数据,我想使用 *ngFor 来显示它们。我已经在正确的时间正确输入了数据,但它说

ERROR Error: "Cannot find a differ supporting object '[object Promise]' of type 'object'. NgFor only supports binding to Iterables such as Arrays."

不过据我所知,在最近版本的 Angular 中,您可以在 *ngFor 中使用 json。

JSON 返回:https://pastebin.com/TTn0EqSS

app.component.html

<div [class.app-dark-theme]="true">
    <mat-sidenav-container fullscreen class="sidenav-container">
        <mat-toolbar class="toolbar">
            Coin Market Cap 3rd party api app
        </mat-toolbar>

        <mat-card>
            <mat-card-header>
                <mat-card-title>CryptoCurrency Market Overview</mat-card-title>
                <mat-card-subtitle>Top 15 current currencies.</mat-card-subtitle>
            </mat-card-header>

            <mat-card-content class="currency-listings">
                <div *ngIf="finishedLoading">
                    <mat-grid-list cols="1" rowHeight="2:1">
                        <mat-grid-tile *ngFor="let currency of currenciesJson; let i = index" (click)="selectCurrency(i)"> 
                            {{currency.data[i].name}}
                        </mat-grid-tile>
                    </mat-grid-list>

                    test 
                    test
                    test
                </div>
            </mat-card-content>
        </mat-card>

        <!--    (click)="showInfo(true)"   -->

        <mat-card *ngIf="displayInfo">
            test
        </mat-card>
    </mat-sidenav-container>
</div>

硬币市场-cap.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class CoinMarketCapService 
{   
    key = "REDACTED";   
    apiUrl = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/';

    constructor(public http: HttpClient) { }

    getCurrencies(totalCurrencies: number)
    {
        let promise = new Promise((resolve, reject) => {
            let url = this.apiUrl + "listings/latest?limit=" + totalCurrencies + "&CMC_PRO_API_KEY=" + this.key;
            this.http.get(url)
            .toPromise()
            .then(
            res => { 
                console.log(res);
                resolve();
            });
        })
        return promise;
    }

    getCurrency(currencyId: number)
    {
        console.log("in getcurrency");
        let url = this.apiUrl + "info?id=" + currencyId + "&CMC_PRO_API_KEY=" + this.key;
        console.log(url);
        return this.http.get(url);
    }
}

app.component.ts

import { Component } from '@angular/core';
import { CoinMarketCapService } from '../services/coin-market-cap.service';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent 
{   
    currenciesJson = {};
    displayInfo = false;
    finishedLoading = false;

    constructor(private CoinMarketCapService: CoinMarketCapService) {}

    ngOnInit()
    {
        console.log(this.currenciesJson);
        this.currenciesJson = this.CoinMarketCapService.getCurrencies(10)
        .then(res => 
            this.finishedLoading = true
        )
        console.log(this.currenciesJson);
        console.log("exiting ngoninit");
    }

    selectCurrency(currencyId: number)
    {
        console.log(currencyId);
        let currencyObject = this.CoinMarketCapService.getCurrency(currencyId);
    }

    showInfo ( showInfo: boolean )
    {
        this.displayInfo = showInfo;
    }
}

ngFor 遍历数组。但是请阅读您的代码:

currenciesJson = {};

在这里你将它初始化为一个对象。

this.currenciesJson = this.CoinMarketCapService.getCurrencies(10)
    .then(res => 
        this.finishedLoading = true
    )

在这里你将它初始化为一个承诺。

所以这不可能行得通。必须是数组。

我强烈建议您阅读 the guide about HTTP 以了解如何正确使用 HttpClient 获取数据。你不应该使用承诺,尤其是你使用它们的方式,它并不完全正确,而且充满了反模式。它应该归结为:

getCurrencies(totalCurrencies: number): Observable<Array<Currency>> {
    const url = this.apiUrl + "listings/latest?limit=" + totalCurrencies + "&CMC_PRO_API_KEY=" + this.key;
    return this.http.get<Array<Currency>>(url);
}

并且,在组件中:

currenciesJson: Array<Currency> = [];
...

this.CoinMarketCapService.getCurrencies(10).subscribe(currencies => this.currenciesJson = currencies);

注意指定类型如何让编译器告诉您代码中的所有错误。

首先避免初始化:
currenciesJson: object; 代替 currenciesJson = {};
并将 ngOnInit 方法更改为:

ngOnInit()
    {
        this.CoinMarketCapService.getCurrencies(10)
        .then(res => {
            this.currenciesJson = res
            this.finishedLoading = true
        });
    }

编辑

<mat-grid-tile *ngFor="let currency of currenciesJson.data; let i = index" (click)="selectCurrency(i)"> 
       {{currency.name}}
</mat-grid-tile>

解决此问题的最简单方法是创建一个 TypeScript 模型对象以将传入的 json 文件映射到。

例如,使用来自 Json 对象的您自己的密钥:

export class CurrencyModel {
    currency: string;
    foo: number;
    bar: number;
    constructor(currency: string, foo: number, bar: number) {
        this.currency = currency;
        this.foo = foo;
        this.bar = bar;
    }

}

然后您将创建对象模型的新实例:

currencyData: CurrencyModel;
getCurrency(currencyId: number)
{
    console.log("in getcurrency");
    let url = this.apiUrl + "info?id=" + currencyId + "&CMC_PRO_API_KEY=" + this.key;
    console.log(url);
    response = this.http.get(url);
    response.subscribe(data => {
        this.currencyData = data;
    }, error1 => {
        console.log(error1);
    })
}

*ngFor 仅用于迭代数组,而不是对象,您的响应包含一个具有状态(对象)和数据(对象数组)的对象。您可以像这样遍历对象数组。

this.currenciesJson = {
  status : {},
  data: [{}]
}

你只能循环currenciesJson.data

尝试设置

<mat-grid-tile *ngFor="let currency of currenciesJson.data; let i = index" (click)="selectCurrency(currency)"> 
   {{currency.name}}
 </mat-grid-tile>

此外,如果使用 angular 6.1 或通过使用 Object.keys() 如果使用 angular 5,您可以通过 keyValue pipr 循环对象键,但我认为您的问题是循环 'data' 而不是 'keys'

在 Angular 6.1.something(最近发布)中有一个用于迭代 JSON 对象的管道。

它调用 KeyValuePipe - 文档在这里 - https://angular.io/api/common/KeyValuePipe

<div *ngFor="let item of object | keyvalue">
  {{item.key}}:{{item.value}}
</div>

这太新了,您必须使用最新版本的 Angular6。如果您已经使用 Angular6,那么只需 运行:

ng update @angular/core
ng update @angular/cli

获取最新消息。

这是一篇关于它的文章 - http://www.talkingdotnet.com/angular-6-1-introduces-new-keyvalue-pipe/

在这个 post 中的评论/答案的帮助下,我达到了我想要的程度。

Angular 这里使用了 6.1s 新的键值管道。 https://angular.io/api/common/KeyValuePipe

它很老套,但就其用途而言还不错。如果我回到这里,我肯定会进步很多,但它确实有效。

这是 3 个文件的来源。

app.component.ts

import { Component } from '@angular/core';
import { CoinMarketCapService } from '../services/coin-market-cap.service';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent 
{   
    currenciesJson: object;
    selectedCurrency: object;
    displayInfo: boolean = false;
    finishedLoading: boolean = false;

    constructor(private CoinMarketCapService: CoinMarketCapService) {}

    ngOnInit()
    {
        this.CoinMarketCapService.getCurrencies(15)
        .then(res => {
            this.currenciesJson = res,
            this.finishedLoading = true;
        });
    }

    selectCurrency(currencyId: number)
    {
        this.CoinMarketCapService.getCurrency(currencyId)
        .then( res => {
            this.selectedCurrency = res,
            this.showInfo(true)
        })
    }

    showInfo ( showInfo: boolean )
    {
        this.displayInfo = showInfo;
    }
}

硬币市场-cap.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class CoinMarketCapService 
{   
    key: string = "REDACTED"; // https://pro.coinmarketcap.com/ free 6000 requests per month.   
    apiUrl: string = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/';

    constructor(public http: HttpClient) { }

    getCurrencies(totalCurrencies: number)
    {
        let promise = new Promise((resolve, reject) => {
            let url: string = this.apiUrl + "listings/latest?sort=market_cap&cryptocurrency_type=coins&limit=" + totalCurrencies + "&CMC_PRO_API_KEY=" + this.key;
            this.http.get(url)
            .toPromise()
            .then(
            res => { 
                resolve(res);
            });
        })
        return promise;
    }

    getCurrency(currencyId: number)
    {
        let promise = new Promise((resolve, reject) => {
            let url: string = this.apiUrl + "info?id=" + currencyId + "&CMC_PRO_API_KEY=" + this.key;
            this.http.get(url)
            .toPromise()
            .then(
            res => { 
                resolve(res);
            });
        })
        return promise;
    }
}

app.component.html

<div [class.app-dark-theme]="true">
    <mat-sidenav-container fullscreen class="sidenav-container">
        <mat-toolbar class="toolbar">
            Coin Market Cap 3rd party api app
        </mat-toolbar>

        <mat-card>
            <mat-card-header>
                <mat-card-title>CryptoCurrency Market Overview</mat-card-title>
                <mat-card-subtitle>Top 15 current currencies.</mat-card-subtitle>
            </mat-card-header>

            <mat-card-content>
                <div *ngIf="finishedLoading">
                    <mat-grid-list cols="1" rowHeight="40px">
                        <mat-grid-tile *ngFor="let currency of currenciesJson.data | keyvalue; let i = index" 
                                (click)="selectCurrency(currency.value.id)" class="grid-tile"> 
                            {{currency.value.name}}
                        </mat-grid-tile>
                    </mat-grid-list>
                </div>
            </mat-card-content>
        </mat-card>

        <mat-card *ngIf="displayInfo">  
            <mat-card-header>
                <mat-card-title>Selected Cryptocurrency Details</mat-card-title>
                <mat-card-subtitle>Name and links of selected currency</mat-card-subtitle>
            </mat-card-header>

            <mat-card-content *ngFor="let currency of selectedCurrency.data | keyvalue; let i = index">
                Name: {{currency.value.name}} <br><br>
                Ticker Symbol: {{currency.value.symbol}}<br><br>
                Website: {{currency.value.urls.website}}<br><br>
                Source Code: {{currency.value.urls.source_code}}<br><br>
                Twitter: {{currency.value.urls.twitter}}
            </mat-card-content>
        </mat-card>
    </mat-sidenav-container>
</div>