【问题标题】:Angular 8 Http Observables in ngOnInit()ngOnInit() 中的 Angular 8 Http Observables
【发布时间】:2019-08-11 12:56:01
【问题描述】:

我还是 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);
        });
    }
}

【问题讨论】:

标签: javascript angular typescript rxjs


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我为我的问题找到的最佳答案实际上是在页面被定向到 (https://angular.io/api/router/Resolve) 之前使用解析器。

    这允许在页面完全呈现之前加载数据,因此没有错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-24
      相关资源
      最近更新 更多