在 Angular 7 中,如何从 Observable 中提取结果?
我正在使用带有 Rail 5 后端的 Angular 7。我有一个用于与 Rails 5 后端交互的服务...
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class CurrencyService {
constructor(private _http: HttpClient) { }
index() {
let ret = this._http.get<Object[]>('api/currencies').map(r => r);
console.log(ret);
return ret;
}
refresh() {
return this._http.get<Object[]>('api/refresh').map(r => r);
}
}
我的 src/app/app.component.ts 文件中有这个,它与服务交互...
import { CurrencyService } from './../shared/currency.service';
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
currencies = [];
title = 'app';
apiStatus: string;
constructor(private _currencySrv: CurrencyService) { }
ngOnInit() {
this._currencySrv.index<Object[]>().subscribe(
currencies => this.currencies = currencies);
}
refresh() {
this._currencySrv.refresh<Object[]>().subscribe(
currencies => this.currencies = currencies);
}
}
如何从返回的 Observable 对象中提取对“index”和“refresh”的调用结果?当我尝试使用 ngFor 迭代“currencies”成员变量时,出现错误
ERROR TypeError: Cannot read property 'name' of undefined
at Object.eval [as updateRenderer] (AppComponent.html:4)
at Object.debugUpdateRenderer [as updateRenderer] (core.js:14735)
at checkAndUpdateView (core.js:13849)
at callViewAction (core.js:14195)
at execComponentViewsAction (core.js:14127)
at checkAndUpdateView (core.js:13850)
at callWithDebugContext (core.js:15098)
at Object.debugCheckAndUpdateView [as checkAndUpdateView] (core.js:14635)
at ViewRef_.detectChanges (core.js:11619)
at eval (core.js:5918)
我认为是因为结果包装在一个名为“data”的字段中,但是当我尝试通过“ng serve”启动 Angular 服务器时,我不知道提取该字段的正确方法,而不会出现各种编译错误。
编辑: 在 src/app/app.component.html 中我有
<!--The content below is only a placeholder and can be replaced.-->
<ul>
<ng-container *ngIf="currencies?.length">
<li *ngFor="let currency of currencies">
<div>{{currency.name}}</div>
<div>{{currency.country}}</div>
<div>{{currency.rate / 100}}</div>
</li>
</ng-container>
</ul>
刷新
但是没有显示任何数据,即使
您的代码中有很多问题需要纠正。@JBNizet 似乎已经提到了大多数问题。
让我们先从您的服务开始。
-
如上所述,如果您在 Angular 应用程序中使用
rxjs6
,则可以使用rxjs6
管道运算符语法,而不是您使用的rxjs5
语法。(来源: docs ) -
将
Observable
的import
位置更改为rxjs
,并将map
更改为rxjs/operators
。 (来源: docs ) -
import { Http } from '@angular/http';
是多余的。此包已弃用。请改用@angular/common/http
。(来源: docs ) -
不要使用
Object
,因为它指的是非原始的装箱对象。(来源: docs )。相反,使用any
或更好的方法是创建一个定义后端响应的接口。 -
您当前的
map
编写方式甚至不执行任何操作。相反,由于您的后端返回一个包含data
的对象,因此您可以使用map
仅返回data
。 -
我不确定您的
refresh
api 做什么,但如果它只是获取与currencies
api 相同的数据,那么它似乎没有必要。我将在我的答案中保留原样,因为我不确定它是否包含其他数据。
考虑到所有这些,您的
CurrencyService
现在将看起来像
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { CurrenciesResponse, Currency } from 'path/to/your/interface';
@Injectable()
export class CurrencyService {
constructor(
private _http: HttpClient
) { }
index(): Observable<Currency[]> {
return this._http.get<CurrenciesResponse>('api/currencies').pipe(
map((res: CurrenciesResponse) => res.data)
);
}
refresh(): Observable<Currency[]> {
return this._http.get<CurrenciesResponse>('api/refresh').pipe(
map((res: CurrenciesResponse) => res.data)
);
}
}
您的接口将看起来像这样
export interface CurrenciesResponse {
data: Currency[];
// Add other backend response properties here if present
}
export interface Currency {
name: string;
country: string;
rate: number;
// ...
}
将您的接口添加到单独的文件中,并在必要时导入它们。
在您的组件中,似乎只有一个问题。当您从
currencyService
调用
index()
和
refresh()
时,您不需要提供类型参数,因为它们不是通用方法。您的 IDE 通常会就此问题发出警告,错误信息如下:
Expected 0 type parameters, but got 1
currencies: Currency[] = [];
title: string = 'app';
apiStatus: string;
constructor(
private _currencySrv: CurrencyService
) { }
ngOnInit() {
this._currencySrv.index().subscribe(currencies => {
this.currencies = currencies;
});
}
refresh() {
this._currencySrv.refresh().subscribe(currencies => {
this.currencies = currencies;
});
}
最后,在您的 HTML 中,
ngIf
是多余的,因为即使
ngFor
也不会打印任何内容,除非
currencies
包含数据。
<ul *ngFor="let currency of currencies">
<li>
<div>{{currency.name}}</div>
<div>{{currency.country}}</div>
<div>{{currency.rate / 100}}</div>
</li>
</ul>
以下是
StackBlitz
上的一个工作示例。我已使用
angular-in-memory-web-api
进行 API 响应。
由于结果包装在对象“数据”中,您需要做的就是在可观察对象发出值时分配所需的对象。
ngOnInit() {
this._currencySrv.index<Object[]>().subscribe(
currencies => this.currencies = currencies.data);
}
refresh() {
this._currencySrv.refresh<Object[]>().subscribe(
currencies => this.currencies = currencies.data);
}
这是因为
currencies
尚未填充数据。因为模板在请求完成之前呈现。将列表包装在
<ng-container *ngIf=“currencies?.length”>
中会有所帮助。
JB Nizet 在评论中写的一切都是绝对正确的。
更新:
app.component.ts
import { CurrencyService } from './../shared/currency.service';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
currencies = [];
title = 'app';
apiStatus: string;
constructor(private _currencySrv: CurrencyService) {
}
ngOnInit() {
this._currencySrv.index().subscribe(
currencies => {
console.log(currencies);
this.currencies = currencies;
});
}
refresh() {
this._currencySrv.refresh().subscribe(currencies => this.currencies = currencies);
}
}
currency.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import 'rxjs/add/operator/map';
import { map } from 'rxjs/operators';
export interface CurrencyWrapper {
data: Currency[];
}
export interface Currency {
name: string,
country: string,
rate: number,
}
@Injectable()
export class CurrencyService {
constructor(private _http: HttpClient) { }
index() {
return this._http.get<CurrencyWrapper>('api/currencies').pipe(map(currencies => currencies.data));
}
refresh() {
return this._http.get<CurrencyWrapper>('api/refresh').pipe(map(currencies => currencies.data));
}
}
注意:最好在单独的文件中定义接口。您还可以根据需要重构代码。