Single Page Web Apps As WordPress
Admin Interfaces Using AngularJS &
The WordPress REST API
Josh Pollock
JoshPress.net - IngotHQ.com -- CalderaWP.com
@Josh412
http://jpwp.me/ingot-admin-js
WHY?
Part One
THIS IS THE
NEW WAY
● WordPress is showing its
age…
● This is how it catches up.
6 Changes, No Page Refresh
State Change != Page Load
Deep Linking With Router
BTW: Angular UI Router > NG Router
Probably, but don't.
Common
UI/UX Pattern
● Portable
● Familiar
Custom REST
APIs In
WordPress
● Easy
● Testable
● Standardized
Why Angular?
● Respects standards &
separation of concerns
● Relatively easy to learn
● Testable
● Someone else pays to
maintain it. #thanksgoogle
Why Angular?
● Accessible dynamic
interfaces are not easy.
● Angular ARIA is a great
start.
HOW?
Part Two
Setting It Up
http://jpwp.me/ingot-admin-load
● Admin page callback prints basic
HTML
● http://jpwp.me/ingot-main-page-partial
● Use wp_localize_script() for:
○ Partials directory path
○ Translation strings
Don't Forget To Make Your API
● Part 3 of my course
● Chapter 8 of my book
● My WordCamp NYC 2015 talk on
WordPress.TV
This Is A Different Talk
Angular UI
Router
● What URL uses what
controller and template?
● http://jpwp.me/ingot-router
ingotApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
//click tests
.state('clickTests', {
url: "/click-tests",
templateUrl: INGOT_ADMIN.partials + "/click-groups.html"
})
.state('clickTests.list', {
url: "/click-tests/all",
templateUrl: INGOT_ADMIN.partials + "/list.html",
controller: 'clickGroups'
} )
.state('clickTests.edit', {
url: "/click-tests/edit/:groupID",
templateUrl: INGOT_ADMIN.partials + "/click-group.html",
controller: 'clickGroup'
} )
.state('clickTests.new', {
url: "/click-tests/new",
templateUrl: INGOT_ADMIN.partials + "/click-group.html",
controller: 'clickGroup'
} )
});
Start It Up
● Include dependencies
● Adding translations to
$rootScope
var ingotApp = angular.module('ingotApp', [
'ui.router',
'ui.bootstrap',
'colorpicker.module',
'ngAria',
'ngResource',
'ngclipboard',
'ngSanitize'
] )
.run( function( $rootScope, $state ) {
$rootScope.translate = INGOT_TRANSLATION;
$rootScope.partials_url = INGOT_ADMIN.partials;
}
);
Angular $http
● Similar to jQuery AJAX
● Use to update $scope and
$state
ingotApp.controller( 'clickDelete', ['$scope', '$http',
'$stateParams', '$state', function( $scope, $http, $stateParams,
$state ){
$http({
url: INGOT_ADMIN.api + 'groups/' + $stateParams.groupID + '?
_wpnonce=' + INGOT_ADMIN.nonce,
method:'DELETE',
headers: {
'X-WP-Nonce': INGOT_ADMIN.nonce
}
} ).then(
function successCallback() {
swal( INGOT_TRANSLATION.deleted, "", "success" );
$scope.group = {};
$state.go('clickTests.list' );
}, function errorCallback( response ) {
var data = response.data;
var text = INGOT_TRANSLATION.sorry;
if( _.isObject( data ) && _.isDefined( data.message )
){
text = data.message;
}
$state.go('clickTests.list' );
}
);
}]);
Factories
● Reusable code for HTTP
● Makes data a injected
dependency -- easily
mocked/ modified
● http://jpwp.me/ingot-factory
ingotApp.factory( 'groupsFactory', function( $resource ) {
return $resource( INGOT_ADMIN.api + 'groups/:id', {
id: '@id',
_wpnonce: INGOT_ADMIN.nonce,
context: 'admin'
},{
'query' : {
transformResponse: function( data, headers ) {
var response = {
data: data,
headers: headers()
};
return response;
}
},
'update':{
method:'PUT',
headers: {
'X-WP-Nonce': INGOT_ADMIN.nonce
}
},
})
});
Factories
● Think of it as your own API
client
● http://jpwp.me/ingot-factory-
in-use
ingotApp.controller( 'clickGroups', ['$scope', '$http',
'groupsFactory', '$sce', function( $scope, $http, groupsFactory, $sce
) {
var page_limit = 10;
$scope.description = $sce.trustAsHtml( INGOT_TRANSLATION.
descriptions.click );
groupsFactory.query( {
page: 1,
limit: page_limit,
context: 'admin',
type: 'click'
}, function ( res ) {
if ( res.data.indexOf( 'No matching' ) > -1 ) {
$scope.groups = {};
return;
};
$scope.groups = JSON.parse( res.data );
var total_groups = parseInt( res.headers[ 'x-ingot-total' ]
);
var total_pages = total_groups / page_limit;
$scope.total_pages = new Array( Math.round( total_pages ) );
$scope.groups.shortcode = [];
} );
}]);
OTHER
BENEFITS?
Part Three
API-Driven Plugins
● REST API Isn't a bolt on
● Easier & alternative method for 3rd-party
integrations
● Jump to SAAS Is Easier
Empower
Others To Be
Providers
Decentralize All The Things!
YOU?
Part Four
GO DO IT!
● Angular's docs are great read them
● More links, slides, examples:
● JoshPress.net/wordcamp-miami-angular/
Questions?
Josh Pollock - JoshPress.net - @Josh412
Ingot - IngotHQ.com - @IngotHQ.com

Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The WordPress REST API

  • 1.
    Single Page WebApps As WordPress Admin Interfaces Using AngularJS & The WordPress REST API Josh Pollock JoshPress.net - IngotHQ.com -- CalderaWP.com
  • 2.
  • 3.
  • 4.
  • 5.
    THIS IS THE NEWWAY ● WordPress is showing its age… ● This is how it catches up.
  • 6.
    6 Changes, NoPage Refresh State Change != Page Load
  • 7.
    Deep Linking WithRouter BTW: Angular UI Router > NG Router
  • 8.
  • 9.
  • 10.
    Custom REST APIs In WordPress ●Easy ● Testable ● Standardized
  • 11.
    Why Angular? ● Respectsstandards & separation of concerns ● Relatively easy to learn ● Testable ● Someone else pays to maintain it. #thanksgoogle
  • 12.
    Why Angular? ● Accessibledynamic interfaces are not easy. ● Angular ARIA is a great start.
  • 13.
  • 14.
    Setting It Up http://jpwp.me/ingot-admin-load ●Admin page callback prints basic HTML ● http://jpwp.me/ingot-main-page-partial ● Use wp_localize_script() for: ○ Partials directory path ○ Translation strings
  • 15.
    Don't Forget ToMake Your API ● Part 3 of my course ● Chapter 8 of my book ● My WordCamp NYC 2015 talk on WordPress.TV This Is A Different Talk
  • 16.
    Angular UI Router ● WhatURL uses what controller and template? ● http://jpwp.me/ingot-router ingotApp.config(function($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise("/"); $stateProvider //click tests .state('clickTests', { url: "/click-tests", templateUrl: INGOT_ADMIN.partials + "/click-groups.html" }) .state('clickTests.list', { url: "/click-tests/all", templateUrl: INGOT_ADMIN.partials + "/list.html", controller: 'clickGroups' } ) .state('clickTests.edit', { url: "/click-tests/edit/:groupID", templateUrl: INGOT_ADMIN.partials + "/click-group.html", controller: 'clickGroup' } ) .state('clickTests.new', { url: "/click-tests/new", templateUrl: INGOT_ADMIN.partials + "/click-group.html", controller: 'clickGroup' } ) });
  • 17.
    Start It Up ●Include dependencies ● Adding translations to $rootScope var ingotApp = angular.module('ingotApp', [ 'ui.router', 'ui.bootstrap', 'colorpicker.module', 'ngAria', 'ngResource', 'ngclipboard', 'ngSanitize' ] ) .run( function( $rootScope, $state ) { $rootScope.translate = INGOT_TRANSLATION; $rootScope.partials_url = INGOT_ADMIN.partials; } );
  • 18.
    Angular $http ● Similarto jQuery AJAX ● Use to update $scope and $state ingotApp.controller( 'clickDelete', ['$scope', '$http', '$stateParams', '$state', function( $scope, $http, $stateParams, $state ){ $http({ url: INGOT_ADMIN.api + 'groups/' + $stateParams.groupID + '? _wpnonce=' + INGOT_ADMIN.nonce, method:'DELETE', headers: { 'X-WP-Nonce': INGOT_ADMIN.nonce } } ).then( function successCallback() { swal( INGOT_TRANSLATION.deleted, "", "success" ); $scope.group = {}; $state.go('clickTests.list' ); }, function errorCallback( response ) { var data = response.data; var text = INGOT_TRANSLATION.sorry; if( _.isObject( data ) && _.isDefined( data.message ) ){ text = data.message; } $state.go('clickTests.list' ); } ); }]);
  • 19.
    Factories ● Reusable codefor HTTP ● Makes data a injected dependency -- easily mocked/ modified ● http://jpwp.me/ingot-factory ingotApp.factory( 'groupsFactory', function( $resource ) { return $resource( INGOT_ADMIN.api + 'groups/:id', { id: '@id', _wpnonce: INGOT_ADMIN.nonce, context: 'admin' },{ 'query' : { transformResponse: function( data, headers ) { var response = { data: data, headers: headers() }; return response; } }, 'update':{ method:'PUT', headers: { 'X-WP-Nonce': INGOT_ADMIN.nonce } }, }) });
  • 20.
    Factories ● Think ofit as your own API client ● http://jpwp.me/ingot-factory- in-use ingotApp.controller( 'clickGroups', ['$scope', '$http', 'groupsFactory', '$sce', function( $scope, $http, groupsFactory, $sce ) { var page_limit = 10; $scope.description = $sce.trustAsHtml( INGOT_TRANSLATION. descriptions.click ); groupsFactory.query( { page: 1, limit: page_limit, context: 'admin', type: 'click' }, function ( res ) { if ( res.data.indexOf( 'No matching' ) > -1 ) { $scope.groups = {}; return; }; $scope.groups = JSON.parse( res.data ); var total_groups = parseInt( res.headers[ 'x-ingot-total' ] ); var total_pages = total_groups / page_limit; $scope.total_pages = new Array( Math.round( total_pages ) ); $scope.groups.shortcode = []; } ); }]);
  • 21.
  • 22.
    API-Driven Plugins ● RESTAPI Isn't a bolt on ● Easier & alternative method for 3rd-party integrations ● Jump to SAAS Is Easier
  • 23.
  • 24.
  • 25.
    GO DO IT! ●Angular's docs are great read them ● More links, slides, examples: ● JoshPress.net/wordcamp-miami-angular/
  • 26.
    Questions? Josh Pollock -JoshPress.net - @Josh412 Ingot - IngotHQ.com - @IngotHQ.com