组件行为主体订阅者不会接收从异步 http 请求发出的值



我试图理解为什么这个行为主体订阅者没有收到发出的值。

在我的组件 ngOnInit 中,我设置了一个服务方法的订阅者,该方法返回对 BehaviorSubject 的引用:

// project-detail.component.ts
ngOnInit() {
this.activatedRoute.params.subscribe((data: Params)  => {
if (data && data.id) {
this.projectService.getProject(data.id).subscribe(project => {
console.log(project);
});
}
});
}

在服务中,我发出一个 http 请求,然后将响应数据推送到 BehaviorSubject 的下一个方法中:

// project.service.ts
import { HttpClient } from '@angular/common/http';  
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable()
export class ProjectService {
private apiUrl = envLocal.apiJsonServerUrl;
private currentProjectObs$ = new BehaviorSubject<IProject>(null);
constructor(private http: HttpClient) {
}
getProject(id: number = 0) {
if (id) {
this.http.get(`${this.apiUrl}/projects/${id}`)
.subscribe(project => {
this.currentProjectObs$.next(project);
});
}
return this.currentProjectObs$;
}

我的理解是,我的组件的ngOnInit中的订阅者应该侦听从getProject返回的BehaviorSubject引用(即currentProjectObs$(的更改。

当ngOnInit第一次运行时,它调用getProject,并且由于getProject中的http调用是异步的,因此返回的初始值为null。但是一旦 http get 请求完成,currentProjectObs$.next(project( 就会被调用,我希望 ngOnInit 中的订阅者会收到新发出的值 - 但这不会发生。

我想了解这里发生了什么,为什么订阅者没有从 http 请求收到异步值,以及如何修复它以便它。

当你使用以下函数时,它将再次返回空值,因为它不会等待 api 调用。 如果你使用的是Behavior subject,你可以直接从行为主体中获取值,你不需要返回它。

getProject(id: number = 0) {
if (id) {
this.http.get(`${this.apiUrl}/projects/${id}`)
.subscribe(project => {
this.currentProjectObs$.next(project);
});
}
return this.currentProjectObs$;
}

您可以使用这两种类型来做到这一点。请将其更改为以下内容:

1.不使用行为主体。

// Your Service 
getProject(id: number = 0): Observable<any> {
if (id) {
return this.http.get(`${this.apiUrl}/projects/${id}`)
.pipe(map(project => {
return project;
// this.currentProjectObs$.next(project);
});
}
return Observable.of(null);
};
// Your Component
ngOnInit() {
this.activatedRoute.params.subscribe((data: Params)  => {
if (data && data.id) {
this.projectService.getProject(data.id)
.subscribe(project => {
console.log(project);
});
}
});
}

2. 与行为主题一起使用

// Your service
public currentProjectObs$ = new BehaviorSubject<IProject>(null);
getProject(id: number = 0) {
if (id) {
this.http.get(`${this.apiUrl}/projects/${id}`)
.pipe(map(project => {
this.currentProjectObs$.next(project);
});
}
// return this.currentProjectObs$;
}
// Your component
this.activatedRoute.params.subscribe((data: Params)  => {
if (data && data.id) {
this.projectService.getProject(data.id);
}
});
this.projectService.currentProjectObs$
.subscribe(project => {
if(project){
console.log(project);
}
})

我认为您的代码可以简化一点:

// project-detail.component.ts
ngOnInit() {
this.activatedRoute.params.pipe(switchMap(params => {
if (data && data.id) {
return this.projectService.getProject(data.id)    
}
// return observable of undefined in case id was not found
return of(undefined);
})).subscribe(actualData  => {
console.log("actual data returned by api: ", actualData)
});
}

在您的服务中:

// project.service.ts
import { HttpClient } from '@angular/common/http';  
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable()
export class ProjectService {
private apiUrl = envLocal.apiJsonServerUrl;
constructor(private http: HttpClient) {
}
getProject(id: number = 0) {
return this.http.get(`${this.apiUrl}/projects/${id}`);
}

只有在您实际需要数据来执行某些操作时,您才应该在最后订阅。数据可以使用 Rxjs 运算符在一个可观察对象之间流动到另一个可观察对象。切换映射订阅第一个可观察的,并返回我们在最后订阅的另一个。

让我知道这是否适合您。

最新更新