27
Feb
2019

Creating an Ionic 4 Application with WordPress Rest API – Part 2

Part 1 can be found here

Showing the featured image on the home page

First update the rxjs imports in the wordpress-restapi.service.ts with the following imports.

import { Observable, forkJoin, throwError, empty, of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';

At the bottom of the wordpress-restapi.service.ts add a new Media class object to hold a posts featured media data.

export class Media {
  date: string;
  date_gmt: string;
  guid: object;
  id: number;
  link: string;
  modified: string;
  modified_gmt: string;
  slug: string;
  status: string;
  type: string;
  title: object;
  author: number;
  comment_status: string;
  ping_status: string;
  meta: object;
  template: string;
  alt_text: string;
  caption: object;
  description: object;
  media_type: string;
  mime_type: string;
  media_details: object;
  post: number;
  source_url: string;

  constructor(values: Object = {}) {
    Object.assign(this, values);
  }

  hasThumbnail() {
    if (Object.keys(this).length > 0) {
      return true;
    }

    return false;
  }

  getThumbnail(size: string = 'thumbnail') {
    if (this.media_details.hasOwnProperty('sizes')) {
      if (size == 'full') {
        return this.media_details['sizes'].full.source_url;
      }

      if (size == 'medium') {
        return this.media_details['sizes'].medium.source_url;
      }
      
      return this.media_details['sizes'].thumbnail.source_url;
    }

    return;
  }
}

Then update the getRecentPosts() method with the following.

getRecentPosts(categoryId: number, page: number = 1): Observable<any> {
    // Get posts by a category if a category id is passed
    let category_url = categoryId ? ("&categories=" + categoryId) : "";

    return this.httpClient.get(this.baseRestApiUrl + "posts?page=" + page + category_url).pipe(
      map((res: any) => res),
      mergeMap((posts: Post[]) => {
        if (posts.length > 0) {
          return forkJoin(
            posts.map((post: Post) => {
              if (post.featured_media === 0) {
                post.media = new Media;
                return of(new Post(post));
              }
              else {
                return this.httpClient.get(this.baseRestApiUrl + "media/" + post.featured_media).pipe(
                  map((res: any) => {
                    post.media = new Media(res);
                    return new Post(post);
                  }),
                  catchError(val => of(val))
                );
              }
            })
          );
        }
        return empty();
      }),
      catchError(val => of(val))
    );
  }

The updated method uses mergmap and forkJoin to get a posts media if it has any.

In the home.page.html file update the ion-item with the following code snippet.

<ion-item *ngFor="let post of posts" (click)="openPost(post.id)">
      
      <ion-thumbnail *ngIf="post.media.hasThumbnail()" slot="end">
          <ion-img [src]="post.media.media_details.sizes.thumbnail.source_url"></ion-img>
      </ion-thumbnail>

      <ion-label [innerHTML]="post.title.rendered" text-wrap></ion-label>
      <span [innerHTML]="post.date | date:longDate" class="post-date"></span>
    </ion-item>

If there is a featured image to show then it will add a thumbnail image to the end of the ion-item.

Showing the featured image, categories, tags and comments on the post page.

Update the wordpress-restapi.service.tss with class objects for Tag, Category and Comment.


export class Tag {
  id: number;
  count: number;
  description: string;
  link: string;
  name: string;
  slug: string;
  taxonomy: string;
  meta: object;

  constructor(values: Object = {}) {
    Object.assign(this, values);
  }
}

export class Category {
  id: number;
  count: number;
  description: string;
  link: string;
  name: string;
  slug: string;
  taxonomy: string;
  parent: number;
  meta: object;

  constructor(values: Object = {}) {
    Object.assign(this, values);
  }
}

export class Comment {
  id: number;
  author: number;
  author_email: string;
  author_ip: string;
  author_name: string;
  author_url: string;
  author_user_agent: string;
  content: object;
  date: string;
  date_gmt: string;
  link: string;
  parent: number;
  post: number;
  status: string;
  type: string;
  author_avatar_urls: object;
  meta: object;

  constructor(values: Object = {}) {
    Object.assign(this, values);
  }
}

Next create methods to get the featured media, tags, categories, and comments from the WordPress API.

  getTags(post) {
    let observableBatch = [];

    post.tags.forEach(tag => {
      observableBatch.push(this.getTag(tag));
    });

    return forkJoin(observableBatch);
  }

  getTag(tagId: number): Observable<Tag> {
    return this.httpClient.get(this.baseRestApiUrl + "tags/" + tagId).pipe(
      map(tag => {
        return new Tag(tag);
      }),
      catchError(val => of(val))
    );
  }

  getComments(postId: number, page: number = 1) {
    return this.httpClient.get(this.baseRestApiUrl + "comments?post=" + postId).pipe(
      map(comments => {
        let commentsArray = [];     

        Object.keys(comments).forEach(function (key) {
          commentsArray.push(new Comment(comments[key]));
        });

        return commentsArray;
      }),
      catchError(val => of(val))
    );
  }

  getCategories(post) {
    let observableBatch = [];

    post.categories.forEach(category => {
      observableBatch.push(this.getCategory(category));
    });

    return forkJoin(observableBatch);
  }

  getCategory(categoryid: number): Observable<Category> {
    return this.httpClient.get(this.baseRestApiUrl + "categories/" + categoryid).pipe(
      map(category => {
        return new Category(category);
      }),
      catchError(val => of(val))
    );
  }

  getMedia(mediaId: number): Observable<Media> {
    if (mediaId > 0) {
      return this.httpClient.get(this.baseRestApiUrl + "media/" + mediaId).pipe(
        map(media => {
          return new Media(media);
        }),
        catchError(val => of(val))
      );
    }
    return of(new Media);
  }

Now update the getPost() method in wordpress-restapi.service.ts to get the featured media, tags, categories and comments along with the post data.

  getPost(postId) {
    return this.httpClient.get(this.baseRestApiUrl + "posts/" + postId).pipe(
      map((res: any) => res),
      flatMap((post: any) => {
        return forkJoin(
          of(new Post(post)),
          this.getComments(post.id),
          this.getMedia(post.featured_media),
          this.getTags(post),
          this.getCategories(post)
        )
      })
    );
  }

Next update the post.page.html to display the post along with the featured image, categories, tags and comments.

<ion-header>
  <ion-toolbar>
    <ion-title>Post</ion-title>

    <ion-buttons slot="start">
      <ion-back-button defaultHref="/"></ion-back-button>
    </ion-buttons>

  </ion-toolbar>
</ion-header>

<ion-content padding>

  <figure *ngIf="media.hasThumbnail()">
    <ion-img [src]="media.getThumbnail('full')"></ion-img>
  </figure>

  <h1 *ngIf="post.title" [innerHTML]="post.getTitle()"></h1>
  <p *ngIf="post.date" [innerHTML]="post.getDate()"></p>

  <p *ngIf="categories.length">
    Posted in: <span *ngFor="let category of categories; let isLast=last">{{category.name}}{{isLast ? '' : ', '}}</span>
  </p>

  <p *ngIf="tags.length">
    Tagged: <span *ngFor="let tag of tags; let isLast=last">#{{tag.name}}{{isLast ? '' : ', '}}</span>
  </p>

  <div *ngIf="post.content" [innerHTML]="post.content.rendered"></div>

  <div *ngIf="comments.length" class="posts-comments-wrapper">
      <p class="bold-title">Comments:</p>

      <ion-item *ngFor="let comment of comments">
        <ion-avatar item-start>
          <img src="{{comment.author_avatar_urls[24]}}">
        </ion-avatar>
        <div>{{comment.author_name}}</div>
        <p [innerHTML]="comment.content.rendered"></p>
      </ion-item>

    </div><!-- /.post-comments-wrapper-->
</ion-content>

Update the post.page.ts file with the following code.

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { LoadingController } from '@ionic/angular';
import { WordPressRestapiService, Post, Media, Tag, Category, Comment } from '../services/wordpress-restapi.service';

@Component({
  selector: 'app-post',
  templateUrl: './post.page.html',
  styleUrls: ['./post.page.scss'],
})
export class PostPage implements OnInit {

  id: string;
  private post: Post = new Post;
  private media: Media = new Media;
  private tags: Tag[] = [];
  private categories: Category[] = [];
  private comments: Comment[] = [];

  constructor(
    public loadingController: LoadingController,
    private route: ActivatedRoute,
    private wordpressService: WordPressRestapiService) { }

  async ngOnInit() {

    const loading = await this.loadingController.create();
    await loading.present();

    this.id = this.route.snapshot.paramMap.get('id');

    this.getPost(this.id).subscribe((data: any) => {
      this.post = data[0];
      this.comments = data[1];
      this.media = data[2];
      this.tags = data[3];
      this.categories = data[4];
      loading.dismiss();
    });
  }

  getPost(postId) {
    return this.wordpressService.getPost(postId);
  }
}

In the next part I’ll look at;

  • Infinite Scroll
  • Browsing by category and tags
Share

You may also like...