Ionic WordPress API – CRUD operation

This articles continuation of our previous post where we have learnt how to authenticate from our ionic application to WordPress back-end.
In this tutorial, we will be further add additional feature to our previous application to perform CRUD operation on WordPress post. We had already add HTTP interceptor to intercept our HTTP request. The JWT token in the header to perform operation like creation, deletion and update. But for GET method we don’t need any authentication.

Ionic CRUD wordpress example
Ionic wordpress CRUD

Creating pages and services for post CRUD operation.
We will have different components for display posts, add and edit post. Let us generate these components with CLI commands first.
$ionic generate page pages/posts
$ionic generate page pages/posts/post
$ionic generate page pages/posts/editPost


We also need two service. The WordPress service to perform post related activities. The common provider where have our common functionality which is shared by large number of component, in our case we called it common service.
$ionic generate service services/wordpress
$ionic generate service services/common

Implementing common service
We have toast functionality which to display toast message which can be shared by components. Add following code in common.service.ts file

import { Injectable } from '@angular/core';
import { ToastController } from '@ionic/angular';

@Injectable()
export class CommonService {

  constructor(private toast: ToastController) { }

  async showToast(message: string, type: string) {
    const toast = await this.toast.create({
      message,
      duration: 3000,
      color: type === 'success' ? 'primary' : 'danger'
    });
    toast.present();
  }
}

Post service to API CRUD implementation
This service use HTTP calls to perform the CRUD operations of post on WordPress site by using HttpClient from @angular/common/http. We have successfully perform post creation, edit and delete.  Below code don’t have featured image implementation, as we need to perform additional task to post media image first and then get its id to add in feature_image as id of the image.

GET /wp-json/wp/v2/posts

The HTTP get method request on WordPress post lack the support of Featured image, as it return only media image id. We can use another endpoint reference as

GET /wp-json/wp/v2/posts?_embed

By simply appending ?_embed to our request will return additional information relating to the post’s like Featured Image, including URL for the various available sizes.

List WordPress post Component
The posts component renders the list of post. We have to three button one to add new post, another to edit post and other to view a post in details. Add following code in posts.page.ts file.

<ion-header>
  <ion-toolbar color="primary">
    <ion-buttons slot="primary">
      <ion-icon slot="icon-only" name="add-outline" routerLink="/posts/new"></ion-icon>
    </ion-buttons>
    <ion-title>Total posts {{ totalPost }}</ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content> 
  <ion-grid class="ion-no-padding">
    <ion-row>
      <ion-col size-sm="4" size="12" *ngFor="let post of posts">
        <ion-card>
          <ion-card-header class="ion-no-padding">
            <img [src]="post.media_url">
          </ion-card-header>
          <ion-card-content>
            <ion-text color="dark">
              <h2 [innerHTML]="post.title.rendered"></h2>
            </ion-text>
            <ion-button color="primary" size="small" fill="outline" class="ion-float-right" (click)="edit(post)">
              <ion-icon name="create-outline" slot="start"></ion-icon> Edit
            </ion-button>
            
            <ion-text>Date : {{ post.date_gmt |date: 'dd MMM yyy' }}</ion-text>
            <div [innerHTML]="post.excerpt.rendered"></div>
            <a class="ion-float-right" [routerLink]="['/', 'posts', post.id]">Read More</a>
          </ion-card-content>
        </ion-card>
      </ion-col>
    </ion-row>
  </ion-grid>

  <ion-infinite-scroll (ionInfinite)="loadMorePost($event)">
    <ion-infinite-scroll-content loadingText="Loading more posts...">
    </ion-infinite-scroll-content>
  </ion-infinite-scroll>
</ion-content>

Our posts component to display all post as

import { Component, OnDestroy } from '@angular/core';
import { WordPressService } from '../../services/wordpress.service';
import { LoadingController } from '@ionic/angular';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';

@Component({
  selector: 'app-posts',
  templateUrl: './posts.page.html',
  styleUrls: ['./posts.page.scss'],
})
export class PostsPage implements OnDestroy {
  posts = [];
  page = 1;
  totalPost: number;
  postsSub: Subscription;

  constructor(
    private loadingCtrl: LoadingController,
    private router: Router,
    private wordpress: WordPressService) { }

  ionViewWillEnter() {
    this.getPosts();
  }

   async getPosts() {
    const loading = await this.loadingCtrl.create({
      message: 'Loading Data...'
    });
    await loading.present();
    this.postsSub = this.wordpress.getPosts().subscribe(data => {
      this.totalPost = this.wordpress.totalPost;
      this.posts = data;
      loading.dismiss();
    });
  }

  async loadMorePost(event) {
    if (this.page >= this.wordpress.pageCount) {
      event.target.disabled = true;
      return;
    }
    this.page++;
    const loading = await this.loadingCtrl.create({
      message: 'Loading Data...'
    });
    await loading.present();
    this.postsSub = this.wordpress.getPosts(this.page).subscribe(data => {
      this.posts = [...this.posts, ...data];
      this.page++;
      event.target.complete();
      loading.dismiss();
    });
  }

  edit(post: any) {
    /** Pass post data to edit page for modification */
    this.router.navigate(['posts/edit/'], {
      state: { post: JSON.stringify(post)}
    });
  }

  ngOnDestroy() {
    this.postsSub.unsubscribe();
  }
}

Implementing EditPost component
We have editPost page, this component we will implement adding new post, edit existing post information like title, date of publish and content of the post and deleting post. For post, put and delete method of HTTP we need user token in HTTP header to performs these operation. In pots page we have add button at top and edit button on each post. We had implement our code in editPost.page.ts

<ion-header>
  <ion-toolbar color="primary">
    <ion-title *ngIf="!post" size="small">Add new post</ion-title>
    <ion-title *ngIf="post" size="small">Edit post</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content>
  <form [formGroup]="form" class="ion-padding">
    <ion-item>
      <ion-label position="floating">{{ 'Title*' }}</ion-label>
      <ion-input formControlName="title"></ion-input>
    </ion-item>
    <ion-item>
      <ion-label position="floating">Date*</ion-label>
      <ion-datetime displayFormat="D MMM, YYYY" formControlName="date" [max]="maxDate"></ion-datetime>
    </ion-item>
    <ion-item>
      <ion-label position="floating">Content*</ion-label>
      <ion-textarea formControlName="content" rows="4"></ion-textarea>
    </ion-item>
  </form>
</ion-content>
<ion-footer>
  <ion-toolbar>
    <ion-button *ngIf="post" color="danger" size="small" (click)="delete()">Delete</ion-button>
    <ion-button color="primary" size="small" class="ion-float-right" (click)="submit(form.value)">
      Submit
    </ion-button>
  </ion-toolbar>
</ion-footer>
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import * as dayjs from 'dayjs';
import { CommonService } from '../../../services/common.service';
import { WordPressService } from 'src/app/services/wordpress.service';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-edit-post',
  templateUrl: './edit-post.page.html',
  styleUrls: ['./edit-post.page.scss'],
})
export class EditPostPage implements OnInit, OnDestroy {
  form: FormGroup;
  post: any;
  postSub: Subscription;
  fileToUpload: File = null;

  constructor(
    private router: Router,
    private formBuilder: FormBuilder,
    private common: CommonService,
    private wordpress: WordPressService
  ) { this.createForm(); }

  ngOnInit() {
    /** Get post date on edit mode */
    if (this.router.getCurrentNavigation().extras.state) {
      const state = this.router.getCurrentNavigation().extras.state;
      this.post = state.post ? JSON.parse(state.post) : '';
    }
    if (this.post) {
      this.form.patchValue({
        title: this.post.title.rendered,
        date: this.post.date,
        content: this.post.content.rendered,
      });
    }
  }

  createForm() {
    this.form = this.formBuilder.group({
      title: ['', Validators.required ],
      date: [dayjs().format()],
      content: ['', Validators.required],
      status: 'publish',
    });
  }

  submit(form) {
    if (!this.form.valid) {
      this.common.showToast('Please fill all required field', 'error');
      return;
    }
    if (this.post) {
      /** Edit mode */
      for (const key in form) {
        if (form.hasOwnProperty(key)) {
          this.post[key] = form[key];
        }
      }
      this.wordpress.updatePost(this.post.id, this.post)
      .subscribe(() => {
        this.common.showToast('Update post successful', 'success');
        this.router.navigateByUrl('/posts');
      });
    } else {
      /** New post */
      this.wordpress.createPost(this.form.value)
      .subscribe(() => {
        this.common.showToast('Post add successful', 'success');
        this.router.navigateByUrl('/posts');
      });
    }
  }

  delete() {
    this.postSub = this.wordpress.deletePost(this.post.id).subscribe(() => {
      this.common.showToast('Delete post successful', 'success');
      this.router.navigateByUrl('');
    });
  }

  ngOnDestroy() {
    if (this.postSub) {
      this.postSub.unsubscribe();
    }
  }
}

Implementing post component
To display post in details we have post component. When user click on read more button on posts page will take you to new page to display post detail information about a post. We have our implementation of post.component.ts as

<ion-header>
  <ion-toolbar>
    <ion-title [innerHTML]="post?.title.rendered"></ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <div *ngIf="post">
    <ion-grid>
      <ion-row>
        <ion-col size-sm="8" offset-sm="2">
          <img [src]="post.media_url" [style.width]="'100%'">
          <div [innerHTML]="post.content.rendered"></div>
        </ion-col>
      </ion-row>
    </ion-grid>
  </div>
</ion-content>
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { WordPressService } from 'src/app/services/wordpress.service';
import { Subscription } from 'rxjs';

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

  constructor(
    private activatedRoute: ActivatedRoute,
    private wordpress: WordPressService
    ) { }

  ngOnInit() {
    const id = this.activatedRoute.snapshot.paramMap.get('id');
    this.postSub = this.wordpress.getPost(id).subscribe(data => {
      this.post = data;
    });
  }

  ngOnDestroy() {
    this.postSub.unsubscribe();
  }
}

Note we have create common post module and routing for all post related component in post page. We also deleted module and routing file of both editPost and post page. We need to import both editPost and postPage component in our post.module.ts file as

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { PostsPageRoutingModule } from './posts-routing.module';
import { PostsPage } from './posts.page';
import { PostPage } from './post/post.page';
import { EditPostPage } from './edit-post/edit-post.page';

@NgModule({
  imports: [
    CommonModule,
    ReactiveFormsModule,
    IonicModule,
    PostsPageRoutingModule
  ],
  declarations: [PostsPage, PostPage, EditPostPage]
})
export class PostsPageModule {}

Our common routing for all post have following implementations.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PostPage } from './post/post.page';
import { PostsPage } from './posts.page';
import { EditPostPage } from './edit-post/edit-post.page';

const routes: Routes = [
  { path: '', component: PostsPage },
  { path: 'new', component: EditPostPage },
  { path: 'edit', component: EditPostPage },
  { path: ':id', component: PostPage },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class PostsPageRoutingModule {}

Our main app-routing.module.ts file have following implementation, we don’t need to import editPost and post routing as both are children route on posts route.

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth/auth.guard';

const routes: Routes = [
  { path: '', redirectTo: 'post', pathMatch: 'full', canActivate: [AuthGuard] },
  {
    path: 'posts', loadChildren: () => import('./pages/posts/posts.module').then( m => m.PostsPageModule), canActivate: [AuthGuard]
  },
  {
    path: 'login',
    loadChildren: () => import('./auth/login/login.module').then( m => m.LoginPageModule)
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }
Spread the love

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top