Batman.js

An Introduction

by Zack Hubert / @zhubert

Hi, I'm Zack Hubert

Developer at PlanningCenterOnline.com

Talk Outline

Why use JS MVC at all?

What is Batman?

How does it work?

Why use JS MVC at all?

Use Turbolinks!

Not the same...

JS MVC is Slow x2

Avoid JS MVC

Time Pressure

Dislike Javascript

Moving Target

Use JS MVC

Performance

API First Development

Getting Truth Out of the DOM - Yehuda Katz

(and into logical JS containers)

Performance

Client Side State

Server Push Changes

Smaller and fewer requests

First Page is just fine

What is Batman?

Batman.js is a framework for building rich web applications with CoffeeScript.

Powerful

App code is concise and favors convention over configuration, and packs a powerful system of view bindings and observable properties.

Like Rails

The API is designed with developer and designer happiness as its first priority, and while it has no real dependencies, it works quite well with Rails.

batmanjs.org

To find out more and to read all that text again :)

Batman's History

  • Initially Released August 23, 2011
  • Built by the fine folks at Shopify
  • Harry Brundage, Nick Small, James MacAulay, Kristian PD, Matt Helm et al.

Batman Now

  • Active Development heading towards 1.0
  • Very Stable API
  • Documentation is improving

How does it work?


  simple_app.js.coffee
  batman/
    controllers/
      posts_controller.js.coffee
    models/
      post.js.coffee
    views/
      posts/
        index.html.haml
        new.html.haml
        show.html.haml
        ...
            

Router

Takes a URL and maps it to a controller action, applies history via pushState or hashBang.

pushState: '/posts/new'

hashBang: '/#!/posts/new'

Example

simple_app.js.coffee


  class SimpleApp extends Batman.App
    @resources 'posts', ->  # index, new, create, show ...
      @resources 'comments'

    @root 'posts#index'
            

Router Notes

  • Concise - idiomatic Rails
  • Supports pushState and hashBang
  • Generates "links" for use in views

Model

Interacts with the storage engine and provides encoding, bindings, observables, accessors, validations, associations, and more.

Example

post.js.coffee


  class SimpleApp.Post extends Batman.Model
    @resourceName: 'post' # minification
    @storageKey: 'posts' # api endpoint
    @persist Batman.RailsStorage

    # associations
    @hasMany "comments"

    # fields
    @encode "title", "content"
    @encode "created_at", Batman.Encoders.railsDate
    @encode "updated_at", Batman.Encoders.railsDate

    # validations
    @validate "title", presence: true
    @validate "content", presence: true
            

Associations

belongs_to


  class SimpleApp.Comment extends Batman.Model
    @belongs_to "post" # @comment.get('post')
            

has_many


  class SimpleApp.Post extends Batman.Model
    @has_many "comments" # @post.get('comments').forEach...
            

has_one


  class SimpleApp.Post extends Batman.Model
    @has_one "thing" # @post.get('thing')
            

plus polymorphic versions

Model Notes

  • Several storage adapters, including Local/REST/Rails
  • Client Side Validations - built-in plus custom
  • Lifecycle state tracking - new, valid, dirty, etc
  • Plus...

Accessors

Encoded attributes


  @get('content')
            

Computed


  @accessor 'last_three_comments'
    get: ->
      last_three = @get('comments.sortedBy.created_at').toArray()
      return last_three.slice(-3,last_three.length)
            

Note the fancy keypath

Observers

For fine grain integration


  @get('post').observe 'updated_at', =>
    # fire whatever you want
            

Controllers

Singletons responsible for interacting with models and rendering views

Example

posts_controller.js.coffee


  class SimpleApp.PostsController extends Batman.Controller
    routingKey: 'posts' # minification

    index: (params) ->
      SimpleApp.Post.load (err,results) =>
        @set 'posts', results

    show: (params) ->
      @set 'post', SimpleApp.Post.find params.id, (err) ->
        throw err if err
      @render source: 'posts/show'

     # snip...
            

Responsibilities

  • Access the models
  • Control flow via redirects
  • Render views
  • ...which register bindings
  • ...which display data

Views

Templates in plain HTML that have data-attributes which bind to Batman Objects, or provide logic-like functionality

Data Binding


    %p{"data-bind" => "post.content"}
             

automatically updates =>


<p data-bind="post.content">
  Bacon ipsum dolor sit amet tenderloin andouille doner, ham hock rump capicola meatball beef turkey tri-tip t-bone pastrami swine.
</p> 
             

This is "truth out of the DOM"

Partials, Iterators, Links

posts/index.html.haml


  %a{"data-route" => 'routes.posts.new'} New Post

  %div{"data-foreach-post" => "posts"}
    .post{"data-partial" => "posts/_post"}
            

posts/_post.html.haml


  %p{"data-bind" => "post.title"}
  %p{"data-bind" => "post.content"}
  %a{"data-route" => "routes.posts[post]"} View details »
             

jQuery Integration

Most Basic Example


  %ul
    %li{"data-foreach-post" => "posts"}
      %div{"data-view" => "PostView"}
        %span{"data-bind" => "post.title"}
            

  class SimpleApp.PostView extends Batman.View
    render: ->
      $(@.get('node')).fireTheThing()
            

Misc

View Filters and Data-*


  %div{"data-showif" => "post.approved"}
    %p{"data-addclass-foo" => "post.foolike"}
    %p{"data-bind" => "post.content | downcase"}
    %span{"data-event-click" => "controllers.posts.show | withArguments post"} View »
             

Demo

Questions?

Thank you!

www.zhubert.com/batman_preso

github.com/zhubert/simple_batman_app - CRUD

github.com/zhubert/fancy_batman_app - ++real time