Introduction to backbone presentation

44
INTRODUCTION TO BACKBONE.JS WITH WORDPRESS / Brian Hogg @brianhogg

description

Introduction to Backbone.js With WordPress

Transcript of Introduction to backbone presentation

Page 1: Introduction to backbone presentation

INTRODUCTION TOBACKBONE.JS WITH

WORDPRESS / Brian Hogg @brianhogg

Page 2: Introduction to backbone presentation
Page 3: Introduction to backbone presentation

AGENDAWhy Backbone.jsBasics of Backbone.js / Underscore.jsEnd-to-end example plugin ( )Github

Page 4: Introduction to backbone presentation

WHO ARE YOU?

Page 5: Introduction to backbone presentation

WHY BACKBONE?Enforces some structure on your JavaScriptEvents system

Page 6: Introduction to backbone presentation

WHY NOT JUST JQUERY?PerformanceLeveraging the communityRe-inventing the wheelCode structure (avoid 1000+ lines of jQuery that "just works")

Page 7: Introduction to backbone presentation

WHAT IS BACKBONE.JS?STRUCTURE (MV*)

Uses jQuery, but only hard requirement is Underscore.js

Page 8: Introduction to backbone presentation

WHAT ISUNDERSCORE.JS?

UTILITY FUNCTIONS WITH __.each_.templateLots more: http://documentcloud.github.io/underscore/

Page 9: Introduction to backbone presentation

TEMPLATESvar template = _.template("hello <%= name %>");var html = template({ name: 'Brian' });console.log( html ); // "hello Brian"

var template = _.template("<strong><%- value %></strong>");var html = template({ value: '<script>' });console.log( html ); // "<strong>&lt;script&gt;</strong>"

Page 10: Introduction to backbone presentation

ALTERNATIVESEMBER.JS, ANGULAR.JS, ...

Multiple ways of doing similar things. Even in Backbone.JS:

“It's common for folks just getting started to treatthe examples listed on this page as some sort ofgospel truth. In fact, Backbone.js is intended to

be fairly agnostic about many common patternsin client-side code.”

http://backbonejs.org/#FAQ-tim-toady

Page 11: Introduction to backbone presentation

BACKBONE /UNDERSCORE

INCLUDED IN WORDPRESS SINCE 3.5The "backbone" of the media manager, revisions UI

Page 12: Introduction to backbone presentation

MODELS“Models are the heart of any JavaScript

application, containing the interactive data aswell as a large part of the logic surrounding it:

conversions, validations, computed properties,and access control. You extend Backbone.Modelwith your domain-specific methods, and Model

provides a basic set of functionality formanaging changes.”

Page 13: Introduction to backbone presentation

MODEL EXAMPLEvar Post = Backbone.Model.extend({ defaults: { title: "", post_status: "draft" }, initialize: function() { console.log("creating a post"); }});

var post = new Post({ title: "Hello, world", post_status: "draft" });

var title = post.get("title"); // Hello, worldvar post_status = post.get("post_status"); // draft

All models have an id attribute for syncing up with a server

Page 14: Introduction to backbone presentation

LISTENING FOR CHANGESpost.on("change:title", function(model) { alert("Title changed to: " + model.get("title"));});

Or in the models initialize with:this.on("change:title", this.titleChanged);

Page 15: Introduction to backbone presentation

VIEWSUsed to turn a model into something you can seeAlways contains a DOM element (the el property), whetherits been added to the viewable page or not

Page 16: Introduction to backbone presentation

BARE MINIMUM TO USE BACKBONEvar PostView = Backbone.View.extend({ events: { "click .edit": "editPost", "change .post_status": "statusChanged" },

editPost: function(event) { // ... }, statusChanged: function(event) { // ... }});

var postView = new PostView({ el: '#my-form' });

Page 17: Introduction to backbone presentation

VIEW EXAMPLEvar PostView = Backbone.View.extend({ tagName: "div", // div by default className: "bbpost", // for styling via CSS events: { "click .edit": "editPost", "change .post_status": "statusChanged" },

initialize: { this.listenTo(this.model, "change", this.render); },

render: { // ... }});

Page 18: Introduction to backbone presentation

RENDERING THE VIEWvar template = _.template($("#tmpl-bbpost").html());var html = template(this.model.toJSON());this.$el.html(html);return this; // for chaining

This uses Underscore.js' _.template, but you can use another!

Page 19: Introduction to backbone presentation

ACCESSING THE DOM ELEMENTthis.$el

this.$el.html()

this.el

// From within a parent viewvar view = new PostView({ model: post });this.$el.append(view.render().el);

this.$this.$('.title').val()

Page 20: Introduction to backbone presentation

COLLECTIONSOrdered set of models

var Posts = Backbone.Collection.extend({ model: Post});

var post1 = new Post({ title: "Hello, world" });var post2 = new Post({ title: "Sample page" });

var myPosts = new Posts([ post1, post2 ]);

Page 21: Introduction to backbone presentation

POPULATING COLLECTIONS FROM THE SERVEROut of the box, Backbone.js supports RESTful APIs throughBackbone.sync(method, model, [options]):

create → POST /collectionread → GET /collection[/id]update → PUT /collection/idpatch → PATCH /collection/iddelete → DELETE /collection/id

Page 22: Introduction to backbone presentation

What Backbone expects when fetching/reading the collection:

[ { id: 1, title: "Hello, world" }, { ... }]

What this sends:wp_send_json_success( array( 'id': 1, 'title': 'Hello, world' ) );

{ success: true, data: [ { id: 1, title: "Hello, world" } ]}

Page 23: Introduction to backbone presentation

So, just override .parse() to accommodate:var Posts = Backbone.Collection.extend({ model: Post, url: ajaxurl, // defined for us if we're in /wp-admin parse: function( response ) { return response.data; }});

// Kick things off$(document).ready(function() { posts = new Posts(); postsView = new PostsView({ collection: posts }); posts.fetch({ data: { action: 'bbpost_fetch_posts' } });});

Or can override .sync(), or even .fetch()

Page 24: Introduction to backbone presentation

Note on calling .fetch() on page load:

“Note that fetch should not be used to populatecollections on page load — all models needed atload time should already be bootstrapped in to

place. fetch is intended for lazily-loading modelsfor interfaces that are not needed immediately:

for example, documents with collections of notesthat may be toggled open and closed.”

http://backbonejs.org/#Collection-fetch

Depends on the situation

Page 25: Introduction to backbone presentation

ROUTERSUsed for routing your application's URLs when using hash tags

(#)

Page 26: Introduction to backbone presentation

(CONTRIVED) EXAMPLEMANAGING WORDPRESS POST TITLES AND PUBLISH/DRAFT

STATUS IN AN ADMIN PANELDEMO

Page 27: Introduction to backbone presentation

DIRECTORY STRUCTUREplugins/ backbone-js-wp-example/ backbone-js-wp-example.php css/ admin.css js/ collections/ posts.js models/ post.js views/ post.js posts.js

Page 28: Introduction to backbone presentation

MODELS/POST.JSvar bbp = bbp || {};

(function($){ bbp.Post = Backbone.Model.extend({ });})(jQuery);

Could set defaults here, if creating new posts

Page 29: Introduction to backbone presentation

BACKBONE-JS-WP-EXAMPLE.PHP/*Plugin Name: Backbone.js WP ExamplePlugin URI:Description: Basic Backbone.js Example in WordPress to edit basic Post propertiesVersion: 1.0Author: Brian HoggAuthor URI: http://brianhogg.comLicense: GPL2*/

define( 'BBPOST_VERSION', 1 );

Page 30: Introduction to backbone presentation

BACKBONE-JS-WP-EXAMPLE.PHPSETTING UP ACTIONS

class BBPostAdmin { public function __construct() { if ( is_admin() ) { add_action( 'wp_ajax_bbpost_fetch_posts', array( &$this, 'ajax_fetch_posts' ) ); add_action( 'wp_ajax_bbpost_save_post', array( &$this, 'ajax_save_post' ) );

if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) { add_action( 'admin_menu', array( &$this, 'admin_menu' ) ); if ( isset( $_GET['page'] ) and 'bbpostadmin' == $_GET['page'] ) { add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_scripts' ) ); } } } }

Page 31: Introduction to backbone presentation

BACKBONE-JS-WP-EXAMPLE.PHPADDING THE MENU

add_menu_page( 'Backbone JS Post Admin Example', 'Backbone JS Post Admin Example', 'add_users'

admin_menu() function

Page 32: Introduction to backbone presentation

BACKBONE-JS-WP-EXAMPLE.PHPADDING THE SCRIPTS

// Add backbone.js models first, then collections, followed by views$folders = array( 'models', 'collections', 'views' );foreach ( $folders as $folder ) { foreach ( glob( dirname( __FILE__ ) . "/js/$folder/*.js" ) as $filename ) { $basename = basename( $filename ); wp_register_script( "$folder/$basename", plugins_url( "js/$folder/$basename", __FILE__ ), wp_enqueue_script( "$folder/$basename" ); }}

wp_register_style( 'bbpost.admin.css', plugins_url( 'css/admin.css', __FILE__ ), false, ECN_VERSION );wp_enqueue_style( 'bbpost.admin.css' );

enqueue_scripts() function

wp-util gives us the wp.ajax helper function

Page 33: Introduction to backbone presentation

ADMIN PAGE TEMPLATE<script id="tmpl-bbpost" type="text/html">

</script>

<h1>Backbone.js WordPress Post Admin Example</h1>

<div id="bbposts"></div>

<div class="bbpost"> <h2> </h2> Post title: <input type="text" class="title" value="<%- title %>" />, Status: <select class="post_status"> <option value=""></option> <option value="publish" <% if ( 'publish' == post_status ) { %>SELECTED <option value="draft" <% if ( 'draft' == post_status ) { %>SELECTED </select> <button>Update</button> </div>

<%- title %>

admin_page()

Page 34: Introduction to backbone presentation

ADMIN PAGE TEMPLATEINDIVIDUAL POST

<div class="bbpost"> <!-- will update when the model updates, automatically --> <h2> </h2> Post title: <input type="text" class="title" value="<%- title %>" />, Status: <select class="post_status"> <option value=""></option> <option value="publish" <% if ( 'publish' == post_status ) { %>SELECTED >Published <option value="draft" <% if ( 'draft' == post_status ) { %>SELECTED >Draft</ </select> <button>Update</button></div>

<%- title %>

<% } %>

Page 35: Introduction to backbone presentation

VIEWS/POSTS.JSvar bbp = bbp || {};

(function($){ bbp.PostsView = Backbone.View.extend({ el: '#bbposts', // Specifying an already existing element

initialize: function() { this.collection.bind('add', this.addOne, this); },

addOne: function(post) { var view = new bbp.PostView({ model: post }); this.$el.append(view.render().el); } });

$(document).ready(function() { bbp.posts = new bbp.PostsCollection(); bbp.postsView = new bbp.PostsView({ collection: bbp.posts }); bbp.posts.fetch({ data: { action: 'bbpost_fetch_posts' } }); });})(jQuery);

Page 36: Introduction to backbone presentation

VIEWS/POST.JSvar bbp = bbp || {};

(function($){ bbp.PostView = Backbone.View.extend({ className: 'bbpost',

initialize: function() { this.model.on("change", this.render, this); },

render: function() { var template = _.template($('#tmpl-bbpost').html()); var html = template(this.model.toJSON()); this.$el.html(html); return this; },

events: { 'click button': 'updatePost' },

updatePost: function() { this.model.set('title', this.$('.title').val()); this.model.set('post_status', this.$('.post_status').val()); this.model.save(); } });})(jQuery);

Page 37: Introduction to backbone presentation

BACKBONE-JS-WP-EXAMPLE.PHPFUNCTION TO SEND THE POST DATA

if ( ! current_user_can( 'edit_published_posts' ) ) wp_send_json_error();

$posts = get_posts( array( 'post_status' => 'any' ));$retval = array();foreach ( $posts as $post ) { $retval[] = array( 'id' => $post->ID, 'title' => $post->post_title, 'post_status' => $post->post_status, );}

wp_send_json_success( $retval );

ajax_fetch_posts()

Page 38: Introduction to backbone presentation

COLLECTIONS/POSTS.JSvar bbp = bbp || {};

(function($){ bbp.PostsCollection = Backbone.Collection.extend({ model: bbp.Post, url: ajaxurl, parse: function ( response ) { // This will be undefined if success: false return response.data; } });})(jQuery);

Page 39: Introduction to backbone presentation

SAVINGOVERRIDE SAVE() IN MODELS/POST.JS

var bbp = bbp || {};

(function($){ bbp.Post = Backbone.Model.extend({ save: function( attributes, options ) { options = options || {}; options.data = _.extend( options.data || {}, { action: 'bbpost_save_post', data: this.toJSON() }); var deferred = wp.ajax.send( options ); deferred.done( function() { alert('done'); }); deferred.fail( function() { alert('failed'); }); } });})(jQuery);

Page 40: Introduction to backbone presentation

BACKBONE-JS-WP-EXAMPLE.PHPSAVING A POST TITLE/STATUS

if ( ! $post = get_post( (int) $_POST['data']['id'] ) ) wp_send_json_error();

if ( ! current_user_can( 'edit_post', $post->ID ) ) wp_send_json_error();

if ( wp_update_post( array( 'ID' => $post->ID, 'post_title' => $_POST['data']['title'], 'post_status' => $_POST['data']['post_status'], ) ) == $post->ID ) wp_send_json_success();else wp_send_json_error();

ajax_save_post() function

Page 41: Introduction to backbone presentation

Extra work to set up initially, but worth it later on!

Page 42: Introduction to backbone presentation

WP-BACKBONESpecial versions of Backbone.View (wp.Backbone.View)

revisions.view.Frame = wp.Backbone.View.extend({ className: 'revisions', template: wp.template('revisions-frame'), // ...});

Handling of SubViewstemplates use <# #> instead of <% %> (as PHP can see <% %>as code: see for details)See revisions.js for an example

trac

Page 43: Introduction to backbone presentation

RESOURCEShttps://github.com/brianhogg/backbone-js-wp-example

http://backbonejs.org/

http://backbonetutorials.com/

https://github.com/addyosmani/backbone-fundamentals

http://kadamwhite.github.io/talks/2014/backbone-wordpress-wpsessions

WordPress revisions.js

Page 44: Introduction to backbone presentation

ENJOY! | brianhogg.com @brianhogg