The Perfect Mix to Create a Hybrid App: Cordova/Ionic Framework, AngularJS & WordPress

 

Do we really need an app when we already have a responsive website?

Reasons for building an app

  • Client says: It's cool to have an app (prestige?)
  • The data is already there! WordPress!
  • Whatever :-)

Now that we cleared this up ...

Reasons for a hybrid app

  • The guy who did the WP site can do it :-)
  • Easier to do than the "native" way (Really now? :-)
  • Cool looking mobile app framework like Ionic

To Begin With:

A couple of slides of an Ionic App with default styling. (Custom are only the logo in the header and its color)

 

Except two static pages all content you see is coming from the WordPress site via API.

The Posts View

Default Ionic list view, loading the number of posts set in "Reading" in the WP Admin.

 

Header bar is kept simple. There's only the hamburger (menu) icon, on the right "Search", "Info (leading to the static WordPress pages for this site), plus a Contact form.

Single Post View

There is only the title, post thumbnail and content on it, plus a "Share this" button.

 

Naturally, typical post meta data like author, date, custom fields and so on, could be included.

Sharing Works

For Sharing there's only one Cordova plugin you ever want to use: 

https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin

Ionic Sidemenu

A basic menu. We've got a separate search page, which gets search results directly from WordPress (in near real-time, a second or so delay).

"Contact Us" features a true contact form being processed on the server.

"Find Us" includes a Google Map.

Google Map

Besides the "Contact Us" page featuring the contact form this is the only page NOT powered by WordPress data.

Ionic

Is Ionic Framework the coolest front-end SDK for creating hybrid apps with HTML 5? You'll be the judge!

 

http://ionicframework.com/

AngularJS

Ionic comes with AngularJS, the popular web application Javascript framework maintained by Google.

https://angularjs.org/

 

AngularJS services (factories) will get all the data, and controllers put it into shape.

Isn't that Phonegap?

Phonegap by Nitobi was bought by Adobe in 2011.They have an app "Build" Service. 

 

Cordova by Apache is what's underneath Phonegap:

http://cordova.apache.org/

How do you start?

WordPress JSON API

https://wordpress.org/plugins/json-api/other_notes/

 

WordPress will bring a REST (JSON) API into core at some point.

Head over to Ionic and Get Started:

http://ionicframework.com/getting-started/

  1. You need Node, the Ionic CLI, plus all the necessary dependencies for your platform.
  2. Install Ionic with (on Windows without $):
    $ npm install -g cordova ionic
  3. Start a Project with a sidebar menu:
    $ ionic start myApp sidemenu
     

Run it

$ cd myApp
$ ionic platform add ios
$ ionic build ios
$ ionic emulate ios

Replace ios with android if you build for that platform.

 

For normal testing in the browser simply run this from your project directory:

$ ionic serve

Fetching data with AngularJS $http

AngularJS has an excellent service for communicating with HTTP servers:

https://docs.angularjs.org/api/ng/service/$http

angular.module('myApp.services', []) 
    .factory('MyPostsData', function($http, $q, MyPostsStorage) {
        ....
    }

We Need Factories

Factory 1:

MyPostsData

Purpose:

Setting the var "data" for holding the incoming data, the JSON URL, the post ID, all stuff that our controller later needs.

Factory 2:

MyPostsStorage

Purpose:

Retrieves and stores the posts in JSON, using "angular.fromJSON" and "angular.toJSON"

Generic factory to store incoming posts in HTML 5 Local Storage

Subtitle

.factory('MyPostsStorage', function() {
  return {
    retrieve: function() {
      var myPosts = window.localStorage['myposts'];
      if(myPosts) {
        return angular.fromJson(myPosts);
      }
      return {};
    },
    save: function(myPosts) {
      window.localStorage['myposts'] = angular.toJson(myPosts);
     },
    ...

WordPress Posts Pagination (Make "Load More" work)

JSON returns the number of pages your posts make up in total. This depends on the setting in WP Admin "Reading": "Blog Pages show at most." E.g. if you have 17 posts in total and blog posts to be shown is set at 5, it will read: ""pages":4.

angular.module('myApp.controllers', [])

//Main Posts (Homepage)
.controller('MyPostsCtrl', function($scope, $http, $ionicLoading, MyPostsData, 
MypPostsStorage, $timeout) {

...
    

    $scope.loadPosts = function () {
         $http({method: 'JSONP', url: MyPostsData.getURL() + 'page=' + $scope.page
         + '&callback=JSON_CALLBACK', timeout: 5000}).
         success(function(data) {
...

$scope, $scope, $scope

        success(function(data) {
            $scope.more = data.pages !== $scope.page;
            $scope.posts = $scope.posts.concat(data.posts);
            MyPostsData.setData($scope.posts);
	    MyPostsStorage.save($scope.posts);
            $ionicLoading.hide();
        }).
        error(function() {
            $scope.posts = MyPostsStorage.retrieve().posts;
            MyPostsData.setData(MyPostsStorage.retrieve().posts);
            $ionicLoading.hide();
        });

$scope is the glue between factories and controllers

If the user clicks the "Load More" button the next 5 posts are loaded which are appended to the existing posts storage.

Ok, so posts are in. What next?

First off:

The cool thing is there's no need to bother the WordPress API for more posts data.

No, it isn't

Using the JSON API I would need to do the following requests:

  • Method: get_category_index

    Returns an array of active categories.

  • Method: get_category_posts

    Returns an array of posts/pages in a specific category.

  • Method: get_post

                         Returns a single post object.

Retrieving the post categories

Accessing HTML Local Storage only, using nested angular.forEach to retrieve all categories."Push" adds each found category to the array. 

.controller('CatsCtrl', function($scope, MyPostsStorage) {
    data = MyPostsStorage.retrieve();
    $scope.categories = [];
    angular.forEach(data, function(post, index){
       angular.forEach(post.categories, function(category, index){
            $scope.categories.push(category);
       });
    });
})

How about duplicate entries?

angular-filter.js filters (unique) them based on unique category slugs.

This happens in the view (where you set up the categories list to be shown with links to posts):

<ion-list>
    <ion-item ng-repeat="cat in categories | unique: 'slug' | orderBy:'title'" ...>

One last bit of functionality before we turn to "Views"

How to search your WP site from the app?

Method: get_search_results

Returns an array of posts/pages in response to a search query.

Using ionic.debounce() the search is called only every 500ms.

//Search Form
.controller('SearchCtrl', function($ionicLoading, $scope, $http) {
    var doSearch = ionic.debounce(function(query) {
        var url = 'http://talktechwith.me/api/get_search_results/?search=';
        $http({method: 'JSONP', url: url + $scope.query 
        + '&post_type=post&callback=JSON_CALLBACK', timeout: 5000}).
        success(function(data) {
...

Views

It's all done through AngularJS expressions (you know inside those double braces) in these HTML templates

All Posts View

{{$index}} is set to the post id. See "ng-repeat":

https://docs.angularjs.org/api/ng/directive/ngRepeat

 <card>
    <ion-list>
	<ion-item class="item-thumbnail-left" ng-repeat="post in posts 
          | filter: query" href="#/app/allposts/{{$index}}">
            <img ng-src="data:image/jpeg;base64,{{post.custom_fields.base64thumb[0]}}" alt="
            {{post.title}}" />
	    <span class="item-text-wrap" ng-bind-html="post.title"></span>
            <i class="icon ion-chevron-right icon-accessory"></i>
            <span class="item-text-wrap" ng-bind-html="post.excerpt"></span>
  	</ion-item>

Thumbnail is in base64 (custom field), then you have "post.title", "post.exercpt".

Single Post View

Date format returning from JSON for this post would be: "2014-04-17 10:37:50".

A custom AngularJS filter takes care of that.

Post thumbnail is cached.

<div class="card">
    <div class="item item-text-wrap">
        <h1 ng-bind-html="post.title"></h1>
    </div>
    <div class="item item-text-wrap">
        <p ng-bind-html="post.date | customDateFormat"></p>
    </div>
    <div class="item item-text-wrap single-img">
        <img ng-cache ng-src="{{post.thumbnail_images.singlethumb.url}}" alt="" />
    </div>
        <div class="item item-text-wrap" ng-bind-html="post.content">
    </div>

Issues, Snags on the WordPress side?

Zero.

All that's left to do is include more content (from custom fields). Look into authentication when doing JSON calls. 

What would be cool!

Include actual functionality from plugins (e.g. a calendar on the site, and so on) from within the app. Any ideas?

Issues, Quirks ...

Some were Ionic, some with Cordova plugins. Normally someone already had the issue you had so you find help in the relevant forums. This relates e.g. to implementing Google Maps, Google Analytics etc.

Things I still have to do

Cordova: What to do when the app user is offline, online? Test push (if needed).

Ionic: Styling, solve some display quirks. Add "pull to refresh perhaps using Ion-Refresher.

AngularJS: Probably do some refactoring.

Contact

Thank you!