尝试 *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>
我正在尝试创建一个从端点返回的数据列表,我得到了 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>