Reset Batman.js Object Attributes

by Kevin Thompson

January 28, 2013

Today I was trying to find a way to reset the attributes of an object in Batman.js if, for example, the object was being edited in a model window and the user clicked a "cancel" button.

Since Batman.js already stores the modified keys and their original values in a hash accessible through the dirtyKeys method, I simply needed to iterate over that hash, setting each key equal to its original value:

class App.Model extends Batman.Model
  # ...
  reset: ->
    @get('dirtyKeys').forEach (key, val) => @set(key,val)

This method works great for resetting a single object, but I quickly realized that I often edit related objects within the same modal as their parent (an example being multiple email address objects belonging to a person object). To expand the reset functionality, I started looking into how I might crawl the tree of relationships for a given model.

After a bit of tinkering, Jeff Berg and I eventually came up with the following solution:

class App.Model extends Batman.Model
  # ...
  reset: ->
    @get('dirtyKeys').forEach (key, val) => @set(key,val)
    associations = @constructor._batman.get('associations')
    return unless associations?
    hasAssociations = new Batman.SimpleSet
    hasAssociations = hasAssociations.merge(associations.getByType('hasMany')) if associations.getByType('hasMany')?
    hasAssociations = hasAssociations.merge(associations.getByType('hasOne')) if associations.getByType('hasOne')?
    hasAssociations.forEach (association) =>
      className = association.options.name
      relatedModel = Batman.currentApp?[className]
      objects = relatedModel.get('loaded').indexedBy(association.foreignKey).get(@get('id'))
      objects.forEach (object) => object.reset()

The additional block of code here starts by grabbing all associations on the object that create either a hasMany or a hasOne relationship. For each relevant association found, we then get all loaded objects for that association's model (as to avoid loading objects that have not yet been retrieved or modified) and reset those child objects as well.

Once our reset method is in place, we simply need to call the method on a loaded object:

person = App.Person.find(1)
person.get('name')              #=> 'Sterling Archer'
person.set('name', 'Dutchess')  #=> 'Dutchess'
person.get('name')              #=> 'Dutchess'
person.reset()
person.get('name')              #=> 'Sterling Archer'