5

So Ember Data Model has a deleteRecord() that performs a destroyRecord() without submitting it to the backend.

How do I do save() without submitting it to the backend?

The reason I need it is that I'm using a custom service to batch-save multiple records of different types (models) in one request. I'm successfully sending the requests, and records are persisted on the backend.

But as the request does not go through the Ember Data pipeline, the response from server will be discarded unless I handle it manually.

Basically, I have this in a service:

// Accepts an array of records of mixed types,
// both existing and new
batchSave (records) {             

  this
    .customAjax(records)          // The records are persisted
    .then(payload => {            // Response from the backend with updated records
      store.pushPayload(payload); // Now all records have been updated with their current state

      // Now all the records are in their current state.
      // But they are still dirty!
      // How do I mark them clean and saved
    });

I've seen this but it seems to discard dirty attributes, while I want dirty attributes to become clean.

I've also tried store.didSaveRecord() but after it records are still dirty.

Community
  • 1
  • 1
Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133

3 Answers3

3

This is an extension of @Tom Netzband's proposal with a big more sugar.

First, a mixin for adapters:

// mixins/prevent-save-adapter.js
export default Ember.Mixin.create({
  preventSave: false,

  updateRecord(store, type, snapshot) {
    if (!this.get('preventSave')) 
      return this._super(store, type, snapshot);
    this.set('preventSave', false);
    return true;
  }
});

Then one for models:

// mixins/prevent-save-model.js
export default Ember.Mixin.create({
  saveWithoutSave() {
    var modelName = this.constructor.modelName;
    var adapter   = this.adapterFor(modelName);

    adapter . set('preventSave', true);
    return this.save();
  }
});

The post adapter:

// adapters/post.js
export default ApplicationAdapter.extend(PreventSaveAdapter);

And the post model:

// models/post.js
export default DS.Model.extend(PreventSaveModel, {
  ...
);

Using this:

// controllers/some-controller.js
export default Ember.Controller.extend({

  actions: {
    someAction () {
      (...)
      post.saveWithoutSave();
    }
  }
});

Untested.

  • 2
    Thx! An even better approach is to pass `{adapterOptions: {dontPersist: true}}` into `recrod.save()` and to read the option in your adapter from the snapshot: `if (snapshot.adapterOptions && snapshot.adapterOptions.dontPersist) { return null; }`. If you make this change, I'll accept the answer. Also mention that serializer customization might be required to treat null. Links: https://github.com/emberjs/data/blob/v2.1.0/packages/ember-data/lib/system/store.js#L1287 https://github.com/emberjs/data/blob/v2.1.0/packages/ember-data/lib/system/model/internal-model.js#L229-L231 – Andrey Mikhaylov - lolmaus Nov 17 '15 at 09:12
2

Disclaimer: This isn't an ideal solution and I hope someone can point us both in a better direction.

Edit: torazaburo's solution on this thread seems like the best way to go.


I've run into the same situation and haven't found a great solution. I ended up writing a custom adapter and added a service to just return true in updateRecord if the service had a flag of preventRequest: true.

Example:

// services/prevent-request.js
export default Ember.Service.extend({
  prevent: false // default
});

// adapters/post.js
export default ApplicationAdapter.extend({
  preventSave: Ember.inject.service(),

  updateRecord (store, type, snapshot) {
    if (this.get('preventSave.prevent')) {
      this.set('preventSave.prevent', false);
      return true;
    }

    this._super(store, type, snapshot);
  }
});

// controllers/some-controller.js
export default Ember.Controller.extend({
  preventSave: Ember.inject.service(),

  actions: {
    someAction () {
      (...)
      this.get('preventSave').set('prevent', true);
      post.save();
    }
  }
});
Tom Netzband
  • 1,110
  • 1
  • 6
  • 13
  • This seems like a creative solution. However, it is a little worrisome that `preventSave` is essentially one big local variable. Instead, I would add the `preventSave` flag to each adapter, most likely through a mixin. Then the client could set it by doing `this.adapterFor('model').setPreventSave()`. –  Nov 17 '15 at 05:25
  • Ah good call, thanks. I upvoted your answer, it's a much nicer solution. – Tom Netzband Nov 17 '15 at 11:35
0

According to Ember Guides using store.createRecord() will created the record and add it to the store but it won't make a request to the backend.

Example:

store.createRecord('post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});

The store object is available in controllers and routes using this.store.

Then if you want to persist it, just call save().

Example:

post.save(); // => POST to '/posts'

isDirty means that the record has local changes that have not yet been saved by the adapter. This includes records that have been created (but not yet saved) or deleted.

Dirty states have three child states:

  • uncommitted: the store has not yet handed off the record to be saved.
  • inFlight: the store has handed off the record to be saved, but the adapter has not yet acknowledged success.
  • invalid: the record has invalid information and cannot be send to the adapter yet.

If you want to make a record clean, try something like this (untested by me):

record.get('stateManager').send('becameClean'); 
Community
  • 1
  • 1
Vítor Martins
  • 1,430
  • 4
  • 20
  • 41