Uses jQuery, but only hard requirement is Underscore.js
var template = _.template("hello <%= name %>");
var html = template({ name: 'Brian' });
console.log( html ); // "hello Brian"
var template = _.template("<%- value %>");
var html = template({ value: '<script>' });
console.log( html ); // "<script>"
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
The "backbone" of the media manager, revisions UI
“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.”
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
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);
el
property), whether its been added to the viewable page or not
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' });
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: {
// ...
}
});
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!
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 ]);
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"
}
]
}
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()
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
Used for routing your application's URLs when using hash tags (#)
DEMO
plugins/
backbone-js-wp-example/
backbone-js-wp-example.php
css/
admin.css
js/
collections/
posts.js
models/
post.js
views/
post.js
posts.js
var bbp = bbp || {};
(function($){
bbp.Post = Backbone.Model.extend({
});
})(jQuery);
Could set defaults here, if creating new posts
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' ) );
}
}
}
}
// 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__ ), array( 'jquery', 'backbone', 'underscore', 'wp-util' ), BBPOST_VERSION );
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' );
Or use something like Grunt to concat into one js file
Backbone.js WordPress Post Admin Example
<div class="bbpost">
<h2><%- title %></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>
<option value="draft" <% if ( 'draft' == post_status ) { %>SELECTED<% } %>>Draft</option>
</select>
<button>Update</button>
</div>
var 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);
var 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);
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 );
var 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);
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);
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();
Extra work to set up initially, but worth it later on!
Special versions of Backbone.View (wp.Backbone.View)
revisions.view.Frame = wp.Backbone.View.extend({
className: 'revisions',
template: wp.template('revisions-frame'),
// ...
});
https://github.com/brianhogg/backbone-js-wp-example
https://github.com/addyosmani/backbone-fundamentals
http://kadamwhite.github.io/talks/2014/backbone-wordpress-wpsessions
http://wordpress.tv/2014/11/03/mark-jaquith-backbone-views-in-wordpress/
WordPress revisions.js