javascript - ngOnInit() 中的 Angular 8 Http Observables

标签 javascript angular typescript rxjs

我还是 Angular 新手,目前正在学习 Angular 8。

我正在尝试创建一个简单的 API 通信服务来加载显示所需的数据。我有一个主组件和一个子组件,两者都需要获取数据来加载。

我尝试遵循几个教程,但我的常见问题是组件加载发生在 API HTTP 请求返回之前,导致未定义的数据。

我当前的 API 服务使用 HttpClient 与 API 通信

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';

@Injectable({
    providedIn: 'root'
})
export class ApiService {
    constructor(private http: HttpClient) {}

    getUserFeed(id: number): Observable<Post[]> {
        return this.http
        .get<Post[]>(`${API_URL}/feed`)
        .pipe(
            retry(3),
            catchError(this.handleError)
        );
    }

    getProfile(id: number): Observable<Profile> {
        return this.http
        .get<Profile>(`${API_URL}/profile/${id}`)
        .pipe(
            retry(3),
            catchError(this.handleError)
        );
    }

    handleError(error: any) {
        let errorMessage: string;
        // Set error message
        (error.error instanceof ErrorEvent) ?
            errorMessage = error.error.message :
            errorMessage = `Error Code: ${error.code}\nMessage: ${error.message}`;
        console.log(errorMessage);
        return throwError(errorMessage);
    }
}

API 应该返回我定义的 Posts 数组。

我在我的组件中将其称为

import { Component, OnInit } from '@angular/core';
import { UserService } from '../user/user.service';
import { ApiService } from '../api/api.service';
import { User } from '../user';
import { Post } from '../Post';

@Component({
    selector: 'app-feed',
    templateUrl: './feed.component.html',
    styleUrls: ['./feed.component.css'],
})
export class FeedComponent implements OnInit {
    posts: Post[] = [];
    user: User;
    post: Post;

    constructor(private userService: UserService) {
        this.user = this.userService.user;
    }

    public ngOnInit() {
        this.userService.getUserFeed(this.user.id).subscribe((feed) => {
            this.posts = feed;
            console.log(this.posts);
        });
    }
}

我的组件 HTML 应该循环这些帖子并将帖子传递给我拥有的子组件

<div *ngIf="posts.length">
    <mat-list *ngFor="let post of posts">
        <!-- Post Display -->
        <app-post-display [post]=post></app-post-display>

        <!-- Post Interaction Row -->
        <app-post-interaction-bar [post]=post></app-post-interaction-bar>

        <!-- Comment Preview -->
        <app-comment-preview [post]=post></app-comment-preview>
        <mat-divider></mat-divider>
    </mat-list>
</div>

到目前为止,它似乎正在按预期获取主要组件的帖子。问题出在子组件 app-post-display 中,它执行类似的操作,从 post.authorId 属性获取帖子作者。

我已经声明了一个作者,并且已经在 ngOnInit 中放置了获取作者数据的代码,但我始终在控制台中收到 ERROR TypeError: Cannot read property 'id' of undefined,没有无论我尝试什么,组件似乎都试图在获取作者之前显示。

我需要调整什么才能在加载组件显示之前获取作者数据

import { Component, Input, OnInit } from '@angular/core';
import { UserService } from '../user/user.service';
import { User } from '../user';
import { Post } from '../post';
import { Profile } from '../profile';
import { ApiService } from '../api/api.service';


@Component({
    selector: 'app-post-display',
    templateUrl: './post-display.component.html',
    styleUrls: ['./post-display.component.css'],
})
export class PostDisplayComponent implements OnInit {
    @Input() post: Post;
    user: User;
    author: Profile;

    constructor(private userService: UserService, private backend: BackendService) {
        this.user = this.userService.user;
    }

    ngOnInit() {
        this.backend.getProfile(this.post.authorId).subscribe((profile) => {
            this.author = profile;
            console.log(this.author);
        });
    }
}

最佳答案

子组件的

ngOnInit只会运行一次。另外,您不能指望它获得最初定义的 post

要修复此问题,您应该将调用移至 ngOnChanges 并首先检查 post 是否已定义。在这里,尝试一下:

import { Component, Input, OnChanges } from '@angular/core';
import { UserService } from '../user/user.service';
import { User } from '../user';
import { Post } from '../post';
import { Profile } from '../profile';
import { ApiService } from '../api/api.service';

@Component({
  selector: 'app-post-display',
  templateUrl: './post-display.component.html',
  styleUrls: ['./post-display.component.css'],
})
export class PostDisplayComponent implements OnChanges {
  @Input() post: Post;
  user: User;
  author: Profile;

  constructor(
    private userService: UserService, 
    private backend: BackendService
  ) {
    this.user = this.userService.user;
  }

  ngOnChanges() {
    if (this.post) {
      this.backend.getProfile(this.post.authorId).subscribe((profile) => {
        this.author = profile;
        console.log(this.author);
      });
    }
  }
}

或者,您可以在父组件中执行此操作:

<div *ngIf="posts">
    <mat-list *ngFor="let post of posts">
        <!-- Post Display -->
        <app-post-display [post]=post></app-post-display>

        <!-- Post Interaction Row -->
        <app-post-interaction-bar [post]=post></app-post-interaction-bar>

        <!-- Comment Preview -->
        <app-comment-preview [post]=post></app-comment-preview>
        <mat-divider></mat-divider>
    </mat-list>
</div>

只要确保您最初没有使用空数组初始化 posts 即可。

关于javascript - ngOnInit() 中的 Angular 8 Http Observables,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57132499/

相关文章:

javascript - d3.js v5.9.1 - 此版本中的某些方法未按预期工作

javascript - 从 json 对象初始化 Typescript 中的 Map<string,string>

Angular 6浏览器检测?

javascript - 通过动态值改变CSS

javascript - 按条件显示/隐藏 Angular 元素

angular - 什么是 Angular 路由?

angular - 如何从子组件打开 MdSidenav?

javascript - 当用户点击下一页时,不确定如何动态检查所有位于一页的单选按钮

javascript - 为什么 $ ("a:focus").length 的计算结果为 0,除非包含在 setTimeout() 中?

javascript - 匹配列表与给定的单词由给定的分隔符列表与单个正则表达式分隔?