I've got an Ember Model, with a field which I want to display and work with, but before I save or retrieve it from the server, I want to multiply/divide it by 1000.
This means the UI should always work with a smaller value, but save a larger value to the server. Likewise, it should retrieve a larger value from the server, but make it smaller before allowing it to be used by the controllers, routes, etc.
// models/my_model.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
value: DS.attr('number')
});
Essentially, I want my application to show these fields as:
/* my webpage */
Please enter name: "foo"
Please enter value: 5
But when I send the request, it should send them as:
{
"my_model": {
"name": "foo",
"value": 5000
}
}
It should also receive them in this format, but then deserialise the value by dividing by 1000.
Please note that multiplying/dividing by 1000 is an example - I may want to append, prepend, add, subtract, etc.
I tried doing this in a less elegant way:
// Controller.js
actions: {
save: function() {
this.set('model.value', this.get('model.value') * 1000);
this.get('model').save().then(
/* stuff */
);
this.set('model.value', this.get('model.value') / 1000);
}
}
But I'm not happy with this, and it results in duplicated code that is hard to maintain, and error-prone.
I have a RESTSerializer
, but I don't know how to use it to manipulate fields, and couldn't find anything in the documentation. Maybe I need to use a different serializer? Mine is basically empty so far.
// serializers/my_model.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
primaryKey: 'name',
});
I would recommend using a Ember Data transform for that use case. It's more reusable and better seperation of concerns than overriding Serializer's normalize
and serialize
methods. An Ember Data transform could be seen as an attribute specific (de-)serializer. There are some built-in transforms: string
, number
, boolean
and date
. You are already using some of them.
You should start with default blueprint to write a custom serializer: ember generate transform currency
. This will generate a file app/transforms/currency.js
and related unit tests. A transform extends DS.Transform
class and must implement serialize
and deserialize
methods. They get the (de-)serialized value as argument and should return the opposite.
Refactoring your example given as an answer here to a transform would look like this one:
// app/transforms/currency.js
import DS from 'ember-data';
export default DS.Transform.extend({
deserialize(serialized) {
return serialized / 1000;
},
serialize(deserialized) {
return deserialized * 1000;
}
});
It could be used like this:
// models/my_model.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
value: DS.attr('currency')
});
Great benefit is that it could be applied to as many attributes as you like in as many models as you need. It could also be unit tested in separation of other serialization concerns and could be easily shared between applications as an Ember Addon.