12
votes

Does anyone know of a way to specify for an Ember model an attribute which is not persisted?

Basically, we're loading some metadata related to each model and sending that data to Ember via the RESTAdapter within the model. This metadata can be changed in our app, but is done via using an AJAX call. Once the call succeeds, I want to be able to update this value within the model without Ember sticking its nose in this business by changing the model to the uncommitted and doing whatever it does with transactions behind the scenes.

I also have the problem that this metadata, which is not data from the model's database record, is passed by the RESTAdapter back to the server, which doesn't expect these values. I am using a RoR backend, so the server errors out trying to mass-assign protected attributes which aren't meant to be attributes at all. I know I can scrub the data received on the server, but I would prefer the client to be able to distinguish between persistent data and auxiliary data.

So, to the original question: is there any alternative to Ember-Data's DS.attr('...') which will specify a non-persistent attribute?

4
See How to prevent Ember Data from saving attribute (ie., Read-only attribute) for how to skip serializing an attribute to the backend.Max Wallace

4 Answers

6
votes

When this PR get's merged it will be possible to flag properties as readOnly. But till then there are some workarounds to this, e.g. overriding your addAttributes method in the Adapter and deal with your special properties, here an example how this could look like:

Define your Model by adding the new option readOnly:

App.MyModel = DS.Model.extend({
  myMetaProperty: DS.attr('metaProperty', {readOnly: true})
});

and then on the Adapter:

App.Serializer = DS.RESTSerializer.extend({
   addAttributes: function(data, record) {
     record.eachAttribute(function(name, attribute) {
       if (!attribute.options.readOnly) {
         this._addAttribute(data, record, name, attribute.type);
       }
     }, this);
   }
 });

what this does is to loop over the attributes of your model and when it find's an attribute with the readOnly flag set it skips the property.

I hope this mechanism works for your use case.

13
votes

The other answers to this question work with Ember data versions up to 0.13, and no longer work. For Ember data 1.0 beta 3 you can do:

App.ApplicationSerializer = DS.RESTSerializer.extend({
  serializeAttribute: function(record, json, key, attribute) {
    if (attribute.options.transient) {
      return;
    }
    return this._super(record, json, key, attribute);
  }
});

Now you can use transient attributes:

App.User = DS.Model.extend({
  name: DS.attr('string', {transient: true})
});

These attributes won't be sent to the server when saving records.

6
votes

Following this answer, to prevent a field from being serialized, override the default serializer for your model:

In app/serializers/person.js:

export default DS.JSONSerializer.extend({
  attrs: {
    admin: { serialize: false }
  }
});

See here for the source PR. This solution works in Ember Data 2, and should work in older versions as well.

1
votes

Update

This answer is most likely out of date with the current releases of Ember Data. I wouldn't use anything in my answer.


I'm answering this question for reference, and because your comment indicated that the record remains isDirty, but here is my solution for read-only, non-persistent, non-dirty attributes.

Overriding the addAtributes method in your Serializer prevents readOnly attributes from being sent to the server, which is probably exactly what you want, but you need to extend (or reopen) your adapter to override the dirtyRecordsForAttributeChange to prevent the record from becoming dirty:

App.CustomAdapter = DS.RESTAdapter.extend({
  dirtyRecordsForAttributeChange: function(dirtySet, record, attrName, newValue, oldValue) {
    meta = record.constructor.metaForProperty(attrName);
    if (meta && meta.options.readOnly) { return; }
    this._super.apply(this, arguments);
  };
});

Then you can use readOnly attributes like so:

App.User = DS.Model.extend({
  name: DS.attr('string', {readOnly: true})
});

user = App.User.find(1);      # => {id: 1, name: 'John Doe'}
user.set('name', 'Jane Doe'); #
user.get('isDirty')           # => false

This setup is working for me.