Build a PWA With Ionic, Angular, and the WordPress REST API - Part 3

In this weekend side-hustle series, we’re building a Progressive Web App (PWA) for desktop and mobile that delivers WordPress content using Ionic, Angular, and the WordPress API. In Part 1, we got things setup and working. In part 2, we created a richer presentation of the WordPress posts using Ionic cards. Today, we’ll implement the single post view (a.k.a. the “content detail view”).

Part 2 Recap

Here’s a quick recap of what we accomplished in Part 2.

  • Replaced Home page list with cards
  • Formatted the post dates using the Angular DatePipe
  • Added featured image to cards (when featured images exist on the post)
  • Made revised source code available on GitHub at

The final result of Part 2 is still pretty simple, but it’s evolving. After Part 2, and starting now, our app looks like this…

Part 3 game plan

The game plan for today is to create a Read More… button on each post preview card. We’ll also create a PostPage component for viewing a single post and we’ll make a parameterized route for it. We’ll pass the post slug as a parameter to the DataService and use that to retrieve the single post for display on the newly created Post page. While we’re at it, we’ll also polish the presentation a bit more.

Add a Read More… button

Next, we’ll add the Read More… button to each post preview card. At the same time, we’ll put in a place holder for displaying the post categories and tags, which we may implement a little later down the road. Update src/app/home/ with the following…

<ion-header> <ion-toolbar> <ion-buttons slot="start"> <ion-menu-button></ion-menu-button> </ion-buttons> <ion-title>Home</ion-title> </ion-toolbar> </ion-header> <ion-content padding> <ion-card *ngFor="let item of items"> <ion-img *ngIf="item._embedded['wp:featuredmedia']" [src]="item._embedded['wp:featuredmedia'][0].source_url"></ion-img> <ion-card-header> <ion-card-subtitle>{{ | date:dateFormat }}</ion-card-subtitle> <ion-card-title [innerHTML]="item.title.rendered"></ion-card-title> </ion-card-header> <ion-card-content> <div [innerHTML]="item.excerpt.rendered"></div> <div> <ion-button href="/{{item.slug}}" routerDirection="root" color="primary" size="small">Read more...</ion-button> </div> Posted in W<br/> Tagged X, Y, Z </ion-card-content> </ion-card> </ion-content>

Now, if you run ionic serve and preview the app, you can see that we have a Read More… button.

Notice that we also have a placeholder for post categories and tags below the Read More… button. I’m not entirely sure that’s where I want it yet, but it’s a reminder for us to think about implementing later.

Add the PostPage component

Next we’ll use the Ionic CLI to generate a PostPage component. This will be the view that is loaded when the user clicks the Read More… button. Within the project root, enter the following command.

ionic generate

From the command-line menu of options, choose Page for the component type and for the Name/path of page:, type Post and hit enter. The Ionic CLI will generate a post directory with the following files inside:

  • post.module.ts

We’ll come back to this component soon, but first, let’s modify the DataService so that it can return a single post by slug.

Update the DataService

Modify src/app/shared/data.service.ts by adding the following function for the PostPage component to call.

getPostBySlug(slug): any { return this.items.find(item => item.slug === slug); }

This takes the post slug and passes it through an RxJS find function, which searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire Observable sequence. In this case, we’ll be searching through the array of posts that have been loaded – returning the one that has the given slug. The slug is the URL segment that identifies the post. We use it to maintain clean URLs that match the WordPress Post name (/%postname%/) permalink structure that we set up in Part 1.

Update the PostPage component

Modify src/app/post/ to be as follows.

import { Component, OnInit } from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import {DataService} from '../shared/data.service'; @Component({ selector: 'app-post', templateUrl: './', styleUrls: ['./'], }) export class PostPage implements OnInit { item: any; constructor(private route: ActivatedRoute, public dataService: DataService) { } ngOnInit() { const itemSlug = this.route.snapshot.paramMap.get('slug'); this.item = this.dataService.getPostBySlug(itemSlug); } }

In the ngOnInit() function, we set the item field based on the item given back from the DataService. This is done by passing the item’s slug which is a route param that we can read out of the ActivatedRoute. So now, we just need to configure the parameterized route.

Configure the parameterized route to the Post page

When we used the Ionic CLI to create the PostPage component, it automatically added the following entry to the routing module (in src/app/app-routing.module.ts).

{ path: 'Post', loadChildren: './post/post.module#PostPageModule' }

Change the path from 'Post' to ':slug'. The colon signifies a named parameter, which we read by name from the ActivatedRoute.

The entire app-routing.module.ts file should  now look like this:

import {NgModule} from '@angular/core'; import {Routes, RouterModule} from '@angular/router'; const routes: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', loadChildren: './home/home.module#HomePageModule' }, { path: 'list', loadChildren: './list/list.module#ListPageModule' }, { path: ':slug', loadChildren: './post/post.module#PostPageModule' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }

And that’s it! If we run the app now with ionic serve, we can click on the Read More… button in a post preview card to load the full post. Here’s what one of our dummy content items looks like now, loaded up into the Post view.

I’m committing this check-point to GitHub with the following message.

Implement single post view

Wrapping up

Building on Part 2, I showed you how you can implement the single post view (a.k.a. the “content detail view”). So, now we can preview a list of WordPress posts and then read any individual one. However,  at this point we’re only able to access the default number of posts returned from our REST API call,  which is ten. In the next part of this series, we’ll implement Ionic’s Infinite Scroll (ion-infinite-scroll) to automatically fetch more posts when the user scrolls to the end. So, stay tuned and remember that the evolving source code can always be cloned from GitHub at

<< Previous: Part 2 Next: Part 4 >>