Robert Mosolgo

To my knowledge, batman.js is not maintained. For that reason, I don't suggest that you use it for a new project!

Batman.js Accessors as Methods

Batman.js is a CoffeeScript front-end MVC framework. One of its core features is accessors, which can be used like properties or methods of a Batman.Object. They can even take arguments!

Batman.Object has properties defined with @accessor in the class definition. Examples of @accessor as accessible properties and computed properties are bountiful. However, I recently learned that accessors can also be made to take arguments, too!

(You can see this example live on at http://jsbin.com/dalodifo/3/edit .)

Definining Accessors with Arguments

To make an accessor that takes arguments, use Batman.TerminalAccessible. Let’s say I have a MathObject which stores a value and allows you to perform calculations on it:

class MathObject extends Batman.Object
  @accessor 'value'

  @accessor 'times', ->
    new Batman.TerminalAccessible (multiplier) =>
      @get('value') * multiplier

Now, my times accessor takes an argument (multiplier) and returns the multiplied value. I pass the argument with get, like this:

fiveObject = new MathObject(value: 5)
fiveObject.get('time').get(10) # => 50
fiveObject.get('time').get(3)  # => 15

Under the hood, fiveObject.get('time') returns a Batman.TerminalAccessible. This object provides source tracking for the function that it calls.

Objects as Arguments

You can also have Batman.Objects as arguments. For example, if we wanted to multiply two MathObjects:

class MathObject extends Batman.Object
  @accessor 'value'

  @accessor 'timesMathObject', ->
    new Batman.TerminalAccessible (mathObj) =>
      @get('times').get(mathObj.get('value'))

Now, the other mathObj will be included in the source tracking. If mathObj.value changes, the value will be recalculated. This is essential for values computed from two Batman.Objects!

What’s the point?

This allows observable “method calls”. It’s wrapped in batman.js source tracking, so whenever the object or the arguments change, the value will be recalculated.

For example, I use it for checking whether a room is at maximum occupancy for certain events:

location.get('isFullFor').get(earlyEvent) # => false
location.get('isFullFor').get(lateEvent)  # => true

When people attend the event (or the location max occupancy is changed), these values are automatically recalculated!

This is the same approach used in batman.js internals for accessing SetIndexes (source).

Accessor Arguments in View Bindings

To pass arguments to accessors in view bindings, you can use the [...] or withArguments filters. Let’s say I want to put this operation in a view binding:

location.get('isFullFor').get(earlyEvent)

[...] is shorthand for calling get with the given argument. I can use it like this:

<span data-bind='location.isFullFor[earlyEvent]'></span>

earlyEvent will be looked up in context and the value will be passed to get, as in the CoffeeScript above.

You can also use the withArguments filter (as of 0.16, PR) like this:

<span data-bind='location.isFullFor | withArguments earlyEvent'></span>

withArguments recognizes that it should use get in this case.