Batman.js is a front-end MVC framework with an unrivaled implementation of key-value observing. I will explore computed properties in batman.js by contrasting them with Ember.js’s computed properties.
- I didn’t write any of the
Batman.Propertycode that makes this feature possible. I’m only a fanboy!
- I don’t know Ember.js. I’ve just gathered examples from the Ember Guides.
To explore computed properties, let’s take the canonical
fullName example. It:
- depends on two other properties,
- returns a string that joins
lastNamewith a space
- can be set
lastNameby splitting on whitespace
We’ll also explore an aggregrated
roster property which:
- depends on
fullNamefor each person
fullName in Ember.js
(This is yanked wholesale from the Computed Properties Guide.)
A couple of things to notice:
fullNameis defined as one function which handles
fullNamemust be told what properties it depends on.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Usage is pretty standard: use
set to access properties.
1 2 3 4
fullName in Batman.js
Two things to notice:
setoperations are defined separately.
fullNamedoesn’t have to be told what its dependencies are.
1 2 3 4 5 6 7 8 9 10 11
The usage is almost identical:
1 2 3 4
roster in Ember.js
(This was adapted from the Computed Properties and Aggregate Data Guide.)
Some things stood out to me:
roster’s properties are declared with a DSL. Array dependencies are limited to one layer deep (ie, you can’t use
mapByis provided by
Ember.Enumerableto handle arrays of objects. Nice!
1 2 3 4 5 6 7 8 9 10 11
roster in Batman.js
Here’s the analogous construction in batman.js:
1 2 3 4 5 6 7 8 9
One thing is the same:
You might notice two big differences:
Batman.Setinstead of a native Array.
rosterdidn’t have to be told what its dependencies are
By using batman.js data structures inside
@accessor functions, we benefit from batman.js’s automatic source tracking. It looks like automatic source tracking was considered by the Ember core team, but deemed impossible or prohibitively expensive.
I recently saw a quote in a React.js talk:
Intellectuals solve probelms. Geniuses prevent them. - Albert Einstein
I think that’s just what the Shopify team did when they implemented
Batman.Observable! The API is very simple and it Just WorksTM.
Pros of batman.js:
@accessorAPI for getters and setters: define
setseparately instead of testing for arguments.
- Automatic dependency tracking: batman.js knows what objects & properties were accessed during computation and observes accordingly.
- There’s no limit to the depth of enumerable dependencies. Any property of a
Batman.Objectthat’s accessed will be tracked, no matter where it exists in the app.
@accessor is the heart and soul of a batman.js app. You’re basically declaring a system of computed properties, then updating that system from user input. Batman.js propagates information to wherever it needs to be.
Cons of batman.js:
- “It’s just not Ember.” You miss out on huge user base, corporate support, and everything that goes with that.
- Beyond that, batman.js resources are sparse. The new guides, cookbook and API docs are improving every week, but for advanced usage you still have to sourcedive sometimes.
- There is a performance hit for global observability. The only place I’ve noticed it is with complex iteration views (batmanjs/batman#1086). I’m hoping to tackle this soon since it’s becoming an issue in PCO Check-ins.
I’m not aware of any features missing from batman.js, but I do miss the “googleability” of a well-traveled path. Batman.js also lacks some of the dev tools like a decent Chrome extension and a command-line client.
I always want to know how things works, so getting in the source is actually a benefit for me.
Six of one, half-dozen of the other:
- Dependency DSL vs
- Calling super:
- External API with
- Cached values in computed properties
- In batman.js, you can opt out of tracking with
Batman.Property.withoutTracking. It’s obscure, but I think it’s ok because batman.js always covers the more common case.
One thing that I found in neither framework was rate-limited properties, a la Knockout. I’d love to have a built-in option for this in batman.js.