简体   繁体   English

ngOnInit()中的Angular 8 Http Observables

[英]Angular 8 Http Observables in ngOnInit()

I'm still new to Angular and learning Angular 8 currently. 我还是Angular的新手,目前正在学习Angular 8。

I'm trying to create a simple API communication Service to load the data needed for display. 我正在尝试创建一个简单的API通信服务来加载显示所需的数据。 I have a main Component with a sub-Component and both need to fetch data to load. 我有一个带有子组件的主组件,两者都需要获取数据才能加载。

I've tried following several tutorials but my common issue is that the Component loading is happening before the API HTTP request is returned, leading to undefined data. 我尝试按照以下几个教程进行操作,但是我的常见问题是在返回API HTTP请求之前会进行组件加载,从而导致未定义的数据。

My current API Service uses HttpClient to communicate with the API 我当前的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);
    }
}

The API should be returning an array of Posts I've defined. API应该返回我定义的Posts数组。

I call this in my component as 我在我的组件中称其为

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);
        });
    }
}

My Component HTML should loop through these posts and pass the post to the sub-Components I have 我的组件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>

So far it seems to be picking up the posts for the main component as expected. 到目前为止,似乎正在按预期接任主要部分的职位。 The issue is in the sub-Component app-post-display which performs a similar action getting the post author from the post.authorId property. 问题出在子组件app-post-display ,该子app-post-display执行类似的操作,从post.authorId属性获取帖子作者。

I've declared an author and I've placed the code to fetch the author data in ngOnInit but I consistently get ERROR TypeError: Cannot read property 'id' of undefined in the console, no matter what I try it seems that the Component is trying to display before the author is fetched. 我已经声明了一个作者,并且将代码放置在ngOnInit中以获取作者数据,但是我始终收到ERROR TypeError: Cannot read property 'id' of undefined无论我尝试执行什么操作,似乎都ERROR TypeError: Cannot read property 'id' of undefined控制台ERROR TypeError: Cannot read property 'id' of undefined ,无论该组件是尝试在提取作者之前显示。

What do I need to adjust to have the author data fetched before the Component display is loaded 我需要调整什么才能在加载组件显示之前获取作者数据

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 of the Child Component will run only once. 子组件的ngOnInit将仅运行一次。 Also, you can't expect it to get the post defined initially. 此外,您不能指望它会得到最初定义的post

To fix it, you should move your call to ngOnChanges and check if post is defined first. 要解决此问题,您应该将调用移至ngOnChanges并检查是否首先定义了post Here, give this a try: 在这里,尝试一下:

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);
      });
    }
  }
}

Alternatively, you can do that in your Parent Component: 或者,您可以在父组件中执行以下操作:

<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>

Just make sure you're not initializing the posts with an empty array initially though. 只需确保您最初不会使用空数组初始化posts

The best answer I found for my problem was actually using a resolver before the page is directed to ( https://angular.io/api/router/Resolve ). 对于该问题,我找到的最佳答案实际上是在将页面定向到( https://angular.io/api/router/Resolve )之前使用解析器。

This allowed the data to be loaded before the page was fully rendered, hence no errors. 这允许在页面完全呈现之前加载数据,因此不会出现错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM