wordpress as the backbone(.js)
TRANSCRIPT
WORDPRESS AS THE BACKBONE(.JS)
https://github.com/beaulebens/WROPE !
@beaulebens !
#wctoga
BEAU LEBENS
AUTOMATTIC
O2
WORDPRESS IS FOR BLOGS
WORDPRESS IS FOR WEBSITES
WORDPRESS IS A CMS
WORDPRESS IS A PUBLISHING PLATFORM
WORDPRESS IS AN APPLICATION BACKEND
WORDPRESS.COM REST APIhttps://developer.wordpress.com/
JSON REST APIhttps://wordpress.org/plugins/json-rest-api/
JSON
JavaScript
Object
Notation
REST
REpresentational
State
Transfer
POST (create)
GET (read)
PUT (update)
DELETE (delete)
http://example.com/wp-json/posts/123/comments
API
Application
Programming
Interface
“Programmatic access to your content in a universal format
via simple HTTP requests”
API JSON
REST
• Read and Write (when authenticated)
• Perform “all” operations
• Any system that talks HTTP + JSON
LET’S BUILD AN APP!
• Mobile first, obvs
• Lightweight/lean (mobile is slow)
• The web is cool, so we’ll use web technologies
• Developer with no WordPress/PHP experience
WROPEWordPress
River
Of
Posts
Experiment
JAVASCRIPT?
BACKBONE.JS• Helps write structured and sane client-side web apps
• Relatively unopinionated/non-prescriptive
• Basic building blocks for better web apps
• Packages Underscore.js for great helper utilities
• Bundled with WP core
• Small (6.5kb! But that’s a bit deceptive, as we’ll see)
• Lots of boilerplate (but very customizable)
https://github.com/WP-API/client-js
ROUTES
Application state via URIs
routerObj: Backbone.Router.extend({ root: '/dev/WROPE/',! routes: { '': 'index', ‘posts/:post’: 'post' },! index: function() { // Get a collection of posts from WP and render them once returned WROPE.fetchPosts( function() { this.renderRiver(); }.bind( this ) ); },! renderRiver: function() { WROPE.postsRiver = new WROPE.postsView( { collection: WROPE.posts } );! // Load optimized inline images, and reload them when the page is resized WROPE.optimizeImageSize(); $(window).on('resize', _.debounce( WROPE.optimizeImageSize, 500 ) ); },! post: function( post ) { if ( null === WROPE.posts ) { WROPE.fetchPosts( function() { this.renderPost( post ); }.bind( this ) ); } else { this.renderPost( post ); } },! renderPost: function( post ) { var thePost = WROPE.posts.get( post ); var postView = new WROPE.postView( { model: thePost, tagName: 'div' } ); $( '#wrope' ).slideUp( function() { $(this).html( postView.$el ).slideDown(); WROPE.optimizeImageSize(); }); return; } }),
• Keep track of where you are in your application
• Allow for history management (Back button!)
• Can use pushState or hash-based URIs
• Fire events on matched routes
• Fire callbacks based on their matched routes
MODELS
Structured key-value data stores (Post, Comment, etc)
/** * Backbone model for single posts */ wp.api.models.Post = BaseModel.extend( _.extend( /** @lends Post.prototype */ { idAttribute: 'ID',! urlRoot: WP_API_Settings.root + '/posts',! defaults: { ID: null, title: '', status: 'draft', type: 'post', author: new wp.api.models.User(), content: '', link: '', 'parent': 0, date: new Date(), date_gmt: new Date(), modified: new Date(), modified_gmt: new Date(), format: 'standard', slug: '', guid: '', excerpt: '', menu_order: 0, comment_status: 'open', ping_status: 'open', sticky: false, date_tz: 'Etc/UTC', modified_tz: 'Etc/UTC', featured_image: null, terms: {}, post_meta: {}, meta: { links: {} } } }, TimeStampedMixin, HierarchicalMixin ) );
• Fire events when something changes
• Keep track of changed values internally
• Backed up by a REST API/endpoint (server magic!)
• Have functions for converting to/from the expected model properties
COLLECTIONS
List of models (Posts, Comments, etc)
/** * Backbone collection for posts */ wp.api.collections.Posts = BaseCollection.extend( /** @lends Posts.prototype */ { url: WP_API_Settings.root + '/posts',! model: wp.api.models.Post } );
• Bubble up model events
• Fire their own events (add, remove, reset)
• Have a ‘comparator’ to dynamically decide sort order
• Have functions for filtering/retrieving specific models
• Backed up by a REST API/endpoint
VIEWS
Visual representation of models/collections
postView: Backbone.View.extend({ tagName: 'li',! className: 'post',! template: _.template( $('#tpl-post').text() ),! events: { 'click a': 'preventDefault', 'click h1.post-title a': 'goToPage', 'click .featured-image a': 'goToPage' },! initialize: function( options ) { this.render(); },! preventDefault: function( e ) { e.preventDefault(); },! goToPage: function() { WROPE.router.navigate( '/' + this.model.get( 'ID' ), { trigger: true } ); return; },! render: function() { this.$el.html( this.template( this.model.attributes ) ); return this; } }),
• Listen to events (on models/collections) and update appropriately
• Handles interactions with the View (clicks etc)
• Correspond to a DOM element (even if it’s not inserted into the page yet)
• Are agnostic to your templating strategy
<script type="text/html" id="tpl-post"> <div class="post-header"> <div class="post-avatar"><img src="<%= author.attributes.avatar %>" width="40" height="40"></div> <h1 class="post-title"><a href="<%= link %>"><%= title %></a></h1> <div class="post-author"><%= author.name %></div> <div class="post-date"><%= date %></div> </div> <div class="post-body"> <% if ( featured_image ) { %> <div class="featured-image"> <a href="<%= link %>"> <img data-src="<%= photon( featured_image.source ) %>" alt="" class="feature"> </a> </div> <% } %> <%= excerpt %> </div> </script>
SPEAKING OF TEMPLATES
HTML templates, delivered in the DOM as script tags
• Built-in ERB-style in Underscore.js
• Token-based replacements (with escaping)
• Basic logic
• Handlebars.js, Mustache.js, etc are also supported
SIDELINE
Routes
Models
Collections
Views/Templates
One more thing…
<?php!// One off hack to allow Cross Origin Resource Sharing from my laptopadd_action( 'wp_json_server_before_serve', function() { // Allow my laptop to talk to WordPress header( 'Access-Control-Allow-Origin: http://beaurrito.local' );! // jQuery sends a preflight OPTIONS request to confirm control headers. // If that's what this request is, then after we've sent the above headers we can bail. if ( 'OPTIONS' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { exit; }});
CORS HACK
On your WP install (mu-plugins)
CODE + DEMO
https://github.com/beaulebens/WROPE !
Beau Lebens @beaulebens http://dntd.cc/