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 MathObject
s:
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.Object
s!
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.