Skip to content
RyanonRails edited this page Jul 4, 2012 · 4 revisions

This section will literally blow your mind. So hold onto your seat. So what exactly is an accessor? An accessor is a property/function that is attached to a Batman.Object. Yes, I realize that's not really amazing or anything, but here's the kicker. The accessor actually "monitors" the @get calls that you use. That means anytime you use @get and the values changes, the model will react to the change. This is huge people.

Before we get ahead of ourselves, let's go over the basic accessors. Here's the most popular accessors for a model (there are others, these are the most used):

  attributes
  dirtyKeys
  errors
  isNew

When a Set it created, these accessors get applied:

  first
  last
  isEmpty
  toArray
  length
  indexedBy
  indexedByUnique
  sortedBy
  sortedByDescending

Model accessors and Set accessors are your most important weapons (from what I've found). So make sure to become familiar with the above accessors as you're going to use them a ton.

Sets are used everywhere, but most importantly they're used when you retrieve data (whether it be from a database, or from localstorage). Have you ever seen code like this and wondered how it works?

<div id="errorz" data-showif="newTemplate.errors.length">

This will show this div(errorz) if newTemplate has errors. Basically, these are chained accessors. The above syntax causes quite a bit of confusion and people think that they are calling "methods", but that's not the case. Remember that because of our 2 way binding we the div(errorz) will show and hide based on having errors.

I recently saw this on the google groups board BAD data-foreach-notice="Notice.all.sortedBy('created_at', 'desc')" BAD in a question. When I first started I did some similar things since the Keypath syntax is kind of weird. Weird but powerful. If anything on the keypath changes, the values will be updated.

John Lynch replied correctly with:

data-foreach-notice="Notice.all.sortedByDescending.created_at"

And if you wanted to sort ascending:

data-foreach-notice="Notice.all.sortedBy.created_at"

If you look in the batman.js code, you'll see that these accessors are "special" accessors that take a key. And that's why we can do "sortedBy.created_at".

  indexedBy
  indexedByUnique
  sortedBy
  sortedByDescending

Maybe you want to do your own accessors. Here's a simple example with custom accessors:

Model:

class WordBump.Word extends Batman.Model
  @resourceName: 'word'
  @persist Batman.LocalStorage
  @encode 'name', 'rank'

  @accessor 'addOneHundred'
    get: -> @get('rank') + 100

Controller:

class WordBump.AppController extends Batman.Controller
  routingKey: 'app'

  index: ->
    # fill the initial wordlist
    @set 'wordList', @fillWordList()


  fillWordList: ->
    wordList = new Batman.Set
    words = ['waffle', 'batman', 'ninja', 'rock', 'paper', 'tree', 'fight', 'tall', 'yellow', 'blue']
    
    # loop through all of the words creating word objects
    for word in words
      wordList.add new WordBump.Word(name: word, rank: 0)

    wordList

View:

<div class="ranking">
  <div data-foreach-word="wordList.sortedByDescending.rank">
    <span data-bind="word.name | prepend 'Word: '"></span>
    <span data-bind="word.addOneHundred"></span>
  </div>
</div>

Here's the basic setup. When the page loads you'd be looking at something like this:

Word: waffle 100  
Word: batman 100  
Word: ninja 100  
Word: rock 100  
Word: paper 100

Now here's where the magic would happen. Let's say I update the rank in the first element of the wordList.

WordBump.get('controllers.app').wordList.toArray()[0].set('rank', 1)

We'll end up with this:

Word: waffle 101  
Word: batman 100  
Word: ninja 100  
Word: rock 100  
Word: paper 100

Because we used a @get within our accessor, the accessor "addOneHundred" saw that the rank was updated and added 100 to it (only shown in the front, the actual model still contains rank: 1). This is really mind blowing, and will save you a ton of time.

You can also put accessors on the class like so:

@classAccessor 'completed', ->
    @get('all').filter (todo) -> todo.get('completed')

Which would update the Todo.completed accessor with the completed todo items as they become completed.

Another example is an invoice. Let's say you're adding rows to an invoice. You could create a class accessor to watch the invoice rows, and when one is added, automatically calculate the GST and the total.

Your life is made easy by a combination of 2 way binding and accessors.

Questions? @RyanonRails