How to autosave changes using Ember Data


#1

I am trying to auto save changes made to a text area.

My first approach was to have a function that fires on change, debounce the calls using ember-concurrently, set the updated model within component and have the save action bubble up to parent.

export default Ember.Component.extend({ 
  saveBody: task(function * (body) { 
    yield timeout(1000); // debounce calls 
    let note = this.get('note');
    note.set('body', body);
    this.sendAction('save', note); // tell parent to save model
  }).restartable(), // run only for the most recent change

  actions: {
    bodyWasUpdated(changed) { // trigged on change
      this.get('saveBody').perform(changed);
    }
  }
});

My problem is that when the input is saved, the model is also updated, so the cursor moves to the start of position rather than staying in place as user continues to type.

My temporary workaround is to use willTransition to save the changes only if user transitions away from the route:

export default Ember.Route.extend({
  ...
  actions: {
    willTransition(transition) {
      let note = this.controller.get('model.note');
      if (note && note.get('hasDirtyAttributes')) {
          this.controller.get('saveLatest').perform();
      }
    }
  }
});

This solution works well if the user stays inside the app, but not if he or she refreshes the page or goes to an external url.

I can’t seem to figure out a way to save changes to a model, without updating the model until the user is done (to prevent cursor movement).

Is there a better way to autosave (or withhold) changes made to models?


#2

in ember 1.13 I did this:

saveOnChange: function() {
    var previousContent = this.get('target.contentToSave');
    if (previousContent && previousContent.get('isDirty') && !previousContent.get('isSaving')) {
      previousContent.save();
    }
    this.set('target.contentToSave', this.get('content'));
  }.observes('content'),

Not sure if it still works this way in Ember 2.0


#3

Thanks for the response. I’m trying to understand your logic so that I can try and implement it into my solution…

From what I understand, you buffered the changes with temporary contentToSave property. Using Ember 2.8, when I set the updated changes, they do not propagate until I save the model, so I don’t think that’s the problem. The cursor only moves to the start position when the model is saved. That’s why I am trying to delay only that part until the user is done making changes to the model.

The !previousContent.get('isSaving') only prevents repeat calls on the model while record is being saved. For that I am using ember-concurrently keepLatest() method when the component calls the parent action. For example:

export default Ember.Controller.extend({
  saveLatest: task(function * (note){ // only enque the latest updated note 
    yield note.save();
  }).keepLatest(),

  actions: {
    saveNote(note){
      this.get('saveLatest').perform(note);
    }
  }
});

Not sure if there’s something else I’m missing here.


#4

Did not know that. Thanks.


#5

Have you looked at https://www.npmjs.com/package/ember-autosave


#6

In today’s Ember Weekly #179 mail is this very interesting article: https://medium.com/@LinehanConor/building-a-save-as-you-type-editor-with-ember-concurrency-3709c56bc266#.4johf8yif

The link title describes pretty well the content…