6. WHY NOT JUST JQUERY?
Performance
Leveraging the community
Re-inventing the wheel
Code structure (avoid 1000+ lines of jQuery that "just works")
7. WHAT IS BACKBONE.JS?
STRUCTURE (MV*)
Uses jQuery, but only hard requirement is Underscore.js
8. WHAT IS
UNDERSCORE.JS?
UTILITY FUNCTIONS WITH _
_.each
_.template
Lots more: http://documentcloud.github.io/underscore/
9. TEMPLATES
var 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><script></strong>"
10. ALTERNATIVES
EMBER.JS, ANGULAR.JS, ...
Multiple ways of doing similar things. Even in Backbone.JS:
“It's common for folks just getting started to treat
the examples listed on this page as some sort of
gospel truth. In fact, Backbone.js is intended to
be fairly agnostic about many common patterns
in client-side code.”
http://backbonejs.org/#FAQ-tim-toady
11. BACKBONE /
UNDERSCORE
INCLUDED IN WORDPRESS SINCE 3.5
The "backbone" of the media manager, revisions UI
12. MODELS
“Models are the heart of any JavaScript
application, containing the interactive data as
well as a large part of the logic surrounding it:
conversions, validations, computed properties,
and access control. You extend Backbone.Model
with your domain-specific methods, and Model
provides a basic set of functionality for
managing changes.”
13. MODEL EXAMPLE
var 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, world
var post_status = post.get("post_status"); // draft
All models have an id attribute for syncing up with a server
14. LISTENING FOR CHANGES
post.on("change:title", function(model) {
alert("Title changed to: " + model.get("title"));
});
Or in the models initialize with:
this.on("change:title", this.titleChanged);
15. VIEWS
Used to turn a model into something you can see
Always contains a DOM element (the el property), whether
its been added to the viewable page or not
16. BARE MINIMUM TO USE BACKBONE
var 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' });
17. VIEW EXAMPLE
var 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: {
// ...
}
});
18. RENDERING THE VIEW
var 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!
19. ACCESSING THE DOM ELEMENT
this.$el
this.$el.html()
this.el
// From within a parent view
var view = new PostView({ model: post });
this.$el.append(view.render().el);
this.$
this.$('.title').val()
20. COLLECTIONS
Ordered 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 ]);
21. POPULATING COLLECTIONS FROM THE SERVER
Out of the box, Backbone.js supports RESTful APIs through
Backbone.sync(method, model, [options]):
create → POST /collection
read → GET /collection[/id]
update → PUT /collection/id
patch → PATCH /collection/id
delete → DELETE /collection/id
22. 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"
}
]
}
23. 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()
24. Note on calling .fetch() on page load:
“Note that fetch should not be used to populate
collections on page load — all models needed at
load time should already be bootstrapped in to
place. fetch is intended for lazily-loading models
for interfaces that are not needed immediately:
for example, documents with collections of notes
that may be toggled open and closed.”
http://backbonejs.org/#Collection-fetch
Depends on the situation
25. ROUTERS
Used for routing your application's URLs when using hash tags
(#)
28. MODELS/POST.JS
var bbp = bbp || {};
(function($){
bbp.Post = Backbone.Model.extend({
});
})(jQuery);
Could set defaults here, if creating new posts
29. BACKBONE-JS-WP-EXAMPLE.PHP
/*
Plugin Name: Backbone.js WP Example
Plugin URI:
Description: Basic Backbone.js Example in WordPress to edit basic Post properties
Version: 1.0
Author: Brian Hogg
Author URI: http://brianhogg.com
License: GPL2
*/
define( 'BBPOST_VERSION', 1 );
30. BACKBONE-JS-WP-EXAMPLE.PHP
SETTING 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' }
}
}
}
31. BACKBONE-JS-WP-EXAMPLE.PHP
ADDING THE MENU
add_menu_page( 'Backbone JS Post Admin Example', 'Backbone JS Post Admin Example', 'add_users'
admin_menu() function
32. BACKBONE-JS-WP-EXAMPLE.PHP
ADDING 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_wp_enqueue_style( 'bbpost.admin.css' );
enqueue_scripts() function
wp-util gives us the wp.ajax helper function
40. BACKBONE-JS-WP-EXAMPLE.PHP
SAVING 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
41. Extra work to set up initially, but worth it later on!
42. WP-BACKBONE
Special versions of Backbone.View (wp.Backbone.View)
revisions.view.Frame = wp.Backbone.View.extend({
className: 'revisions',
template: wp.template('revisions-frame'),
// ...
});
Handling of SubViews
templates use <# #> instead of <% %> (as PHP can see <% %>
as code: see trac
for details)
See revisions.js for an example