Article summary
I have been working with “Ember and Ember Data”:http://emberjs.com/ for the past few projects. We’ve recently had cases where we have a dirty form state (unsaved changes) and we want to restrict actions to prevent data loss.
We’ve come up with a solution that lets us get a rough track on the current state of Ember Data objects without implementing state tracking in every Ember controller. But we warned, it’s not entirely fool-proof, there are some manual parts to the process, and it may require a little fixing to fit your needs.
Ember Store Improvements
DS.Store.reopen
recordsToReset: []
dataWasUpdated: (type, record) ->
recordsToReset = @get('recordsToReset')
return unless $.inArray(record, recordsToReset) == -1
if record.get('isDirty')
changes = record.changedAttributes()
changedKeys = Ember.keys(changes)
if record.get('dirtyType') == "created"
validChanges = false
record.eachAttribute (attr, meta) ->
if attr in changedKeys
hasChanges = false
if meta.options and meta.options.defaultValue
hasChanges = true unless changes[attr][1] == meta.options.defaultValue
else
hasChanges = true
if hasChanges
validChanges = true unless Ember.isBlank(changes[attr][0]) and Ember.isBlank(changes[attr][1])
return unless validChanges
recordsToReset.pushObject(record)
resetDirtyRecords: ->
@get('recordsToReset').forEach (record) ->
while record.get('isDirty')
record.rollback()
@set('recordsToReset', [])
hasDirtyRecords: ->
for dirty in @get('recordsToReset')
changes = dirty.changedAttributes()
!!@get('recordsToReset').findBy('isDirty', true)
Usage
The code above works well for Ember 1.5 and Ember Data 1.0 Beta 7. You can manually call @store.resetDirtyRecords()
to clean up at any point. This would generally follow a call to @store.hasDirtyRecords()
to check if there are dirty records then prompting the user if they want to cancel edits, if there are any. When the user agrees to cancel edits, you can then use @store.resetDirtyRecords()
to clear the changes.
Caveats
Careful! This will clear all changes tracked. If you only want to cancel changes on a certain model, you should call .rollback()
on that individual model. Also, the logic behind the what makes a “dirty record” is as follows:
# A new record has values that are not default. (If you didn’t specify a default, then default is undefined.)
# The change to a blank value must not change to another blank value. (Undefined to blank string changes do not dirty the record.)
# Relationship changes can dirty a record. (Adding a child to a has many will unintentionally dirty a record.)
neat trick, but on Ember 1.5.1 and Ember Data 1.0.0 beta 8, there is a bug that cause the deleted record to still exists on the ember side, even though the data is already deleted on the server side.