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