angularjsonplaceholder

How to output a template of detail post rightly?


I'm write project for learning Angular. My project displays posts from https://jsonplaceholder.typicode.com/. I want to display a detailed post template (on localhost:3000/posts/1 for example). I am getting a specific post at posts.service.ts in method getById. Now I have an error - "Type 'Subscription' has no properties in common with type 'Post'". But I don't know how to fix that. Where is my mistake? How to fix that? How to output a template ({{ post.title }}) rightly?

All project here: posts project

post.service.ts:

import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";

export interface Post {
  title: string;
  userId?: number;
}

@Injectable({ providedIn: "root" })
export class PostService {
  private _postsURL = "https://jsonplaceholder.typicode.com";

  constructor(private http: HttpClient) {}

  public fetchPosts(page: number, itemsPerPage: number): Observable<Post[]> {
    let posts = this.http.get<Post[]>(`${this._postsURL}/posts`);

    return this.getPageItems(posts, page, itemsPerPage);
  }

  private getPageItems(
    posts: Observable<Post[]>,
    page: number,
    itemsPerPage: number
  ): Observable<Post[]> {
    return posts.pipe(
      map((u) => {
        let startIndex = itemsPerPage * (page - 1);
        return u.slice(startIndex, startIndex + itemsPerPage);
      })
    );
  }

  getById(id: number) {
    let post = this.http
      .get<Post[]>(`${this._postsURL}/posts/${id}`)
      .subscribe();

    return post;
  }
}

post.component.ts:

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Params } from "@angular/router";
import { Post, PostService } from "../post.service";

@Component({
  selector: "app-post",
  templateUrl: "./post.component.html"
})
export class PostComponent implements OnInit {
  post: any;

  constructor(
    private route: ActivatedRoute,
    private postsService: PostService
  ) {}

  ngOnInit(): void {
    this.route.params.subscribe((params: Params) => {
      this.post = this.postsService.getById(+params.id);
    });
  }
}

post.component.html:

<div>
    post component
    <h1>{{ post.title }}</h1>
</div>

Solution

  • You can change several things in your code in order for this work and follow best practices. The async pipe will also take care of unsubscribing from the observable when the component is destroyed so you don't have to manually unsubscribe from it.

    Also it is more efficient in terms of change detection.

    In you post.service change getById to return observable:

    Another thing in case you want to see structured HTML json response, you can use "< pre></ pre>" tag.

    getById(id: number): Observable<Post> {
        return this.http.get<Post>(`${this._postsURL}/posts/${id}`);
    }
    

    And post.component.html to:

    <div>
      post component
      <h1>{{ (post | async )?.title }}</h1>
      <pre>{{ (post | async) }}</pre> // you can see structured json
    </div>
    

    Please check fixed sample:

    https://codesandbox.io/s/trusting-drake-zgri7?file=/src/app/post/post.component.ts