【问题标题】:Error trying to *ngFor a Json object尝试 *ngFor 一个 Json 对象时出错
【发布时间】:2018-08-07 17:59:16
【问题描述】:

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

错误错误:“找不到类型为‘object’的不同支持对象‘[object Promise]’。NgFor 仅支持绑定到 Iterables,例如数组。”

不过,据我所知,您可以在最新版本的 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>

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

【问题讨论】:

标签: angular typescript angular-material-6


【解决方案1】:

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

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

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

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

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

获取最新信息。

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

【讨论】:

  • 你有我如何在我的应用程序或它的文档中实现它的例子吗?
  • 我昨晚用了它,效果很好,但最后我改变了我的代码。我会为你找到它并更新我的答案 - 需要几分钟
  • github.com/buffet-time/coinMarketCapApiWebApp(提供密钥 pro.coinmarketcap.com 免费套餐)这是我当前的存储库,由于某种原因,它没有将我从 GET 请求中正确获取的数据分配给我试图将其分配给货币Json 的值。如果我能够做到这一点,那么我认为我将能够使用此功能。如果您知道问题所在,我将非常感谢您的帮助。
  • 我的猜测是您没有更改对象引用。这向 Angular 发出信号表明发生了变化,并且变化检测开始,更新 DOM。获得新引用的一种简单方法是使用来自 lodash ... obj = _.cloneDeep(obj) ... 的 cloneDeep ... 或者对于非常简单的对象 ... obj = {...obj}
【解决方案2】:

解决此问题的最简单方法是创建一个 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);
    })
}

【讨论】:

    【解决方案3】:

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

    currenciesJson = {};
    

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

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

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

    所以这不可能。它必须是一个数组。

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

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

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

    【讨论】:

    • 我将组件和服务更改为您所拥有的,但是如何使用 observable 之后同步执行某些东西,以便我可以启动 *ngFor 并且有什么需要在 *ngFor 中更改与您的实现一起正常工作?
    • 我也忽略了提到 TS 拒绝 Array 在任何使用它的地方,所以我不能那样做。
    • 您当然需要定义该货币类型。由于您的变量名为currencyJson,因此我假设该类型的好名称是Currency。但是您需要定义它,以指定货币是什么。这是 Typescript 的基础知识。退后一步,花点时间了解什么是类型、什么是接口、什么是对象、什么是数组。
    • 从其他答案和我所看到的 Angular 6.1 允许您进入 json 对象中的数据数组时,这样做的目的到底是什么?
    • 我知道什么是类型、接口、对象等。我是 angular/typescript 的新手。它的结构不像 C#/Java。但是我仍然没有看到这样做的目的,因为我所说的可以按照我试图做的方式完成,这就是我所说的。
    【解决方案4】:

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

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

    你只能遍历currencyJson.data

    尝试设置

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

    此外,如果使用 angular 6.1,您可以通过 keyValue pipr 循环对象键,如果使用 angular 5,则可以使用 Object.keys() 循环,但我认为您的问题是循环遍历“数据”而不是“键”

    【讨论】:

    • 我将如何在我当前的应用程序中实现这一点?我愿意稍后再详细了解它的工作原理,但我最好先解决这个问题。
    • 试着按照他的方式去做... { {currency.name}}
    • 尝试我建议的方法来解决您的问题或分享我可以提供帮助的堆栈闪电战!!。
    • github.com/buffet-time/coinMarketCapApiWebApp 这是应用程序的 git 存储库,其中 API 密钥已编辑 (pro.coinmarketcap.com) 免费层每月有 6000 个免费请求
    • 是的,我已经通过代码了,试试上面的方法,它应该适合你
    【解决方案5】:

    首先避免初始化:
    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>
    

    【讨论】:

    • 它正确地返回了数据但是我找不到用 *ngFor now 显示它的方法
    • 我目前拥有的 res 已在控制台中正确登录为已获取数据,但似乎没有正确分配给 currencyJson 变量。 github.com/buffet-time/coinMarketCapApiWebApp 这里是应用程序的 git 存储库,API 密钥已编辑 (pro.coinmarketcap.com) 免费层每月 6000 个免费请求
    【解决方案6】:

    在这篇文章中 cmets/ 答案的帮助下,我达到了我想要的程度。

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

    这很hacky,但它的用途很好。如果我回到这一点,我肯定会进步很多,但它确实有效。

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

    coin-market-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>
    

    【讨论】:

      猜你喜欢
      • 2023-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-20
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多