Ember.js RESTAdapter attributes types - ember.js

RESTAdapter has built in attribute types of string, number, boolean, and date. There are relations to link another models to represent some complex data.
To represent array I need to use transformation or change API from something like this:
["ember.js", "angular.js", "embergular.js"]
to:
[
{
"id": 1,
"ember.js"
},
{
"id": 2,
"angular.js"
},
{
"id": 3,
"embergular.js"
}
]
Which is a little bit overkill... Why there is no built in types like array and object?

IMO the main reason why there aren't such attribute type like array or object is mainly per design.
To represent array I need to use transformation or change API from something like this:
but to represent an array without the needs of building a relation with models you could define a custom transform (what you already mentioned) which don't touches your data. For example to use an array as a model attribute you could do something like this:
DS.RESTAdapter.registerTransform('rawData', {
deserialize: function(serialized) {
return serialized;
},
serialize: function(deserialized) {
return deserialized;
}
});
Then define it in your model like this:
App.MyModel = DS.Model.extend({
myArray: DS.attr('rawData')
});
This way the attribute myArray will be just what your backend returned, an array or object etc.
Hope it helps.

Related

Deserialize Complex Model with Ember Data

I have a single call to the server that returns the whole data of my application
[
{
"id": 1,
"count": 0,
"canGroup": true,
"childs": {
"group": [
{
"id": 0,
"count": 3,
"canGroup": true,
"childs": {
"user": [
{
"id": 0,
"count": 3,
"canGroup": true
}
...
]
}
] ...
}
...
]
How can I do to deserialize this model with Ember Data?
There are two parts to this question. One is how to structure your model to represent this data, the second is how to modify the incoming data so Ember can handle it. The model, let's call it group, would be something like:
// models/group.js
export default DS.Model.extend({
count: DS.attr(),
canGroup: DS.attr(),
childs: DS.hasMany('group')
});
Now we need to tell Ember Data that the children are going to be coming in embedded inside the parent. To do that, we mix in DS.EmbeddedRecordsMixin into the serializer, and also specify an attrs hash, as follows:
// serializers/group.js
export default DS.RESTSerializer(DS.EmbeddedRecordsMixin, {
attrs: {
childs: { embedded: 'always' }
}
However, there is one remaining problem. By default, Ember Data will expect that the childs property contains an array of, well, children. Instead, your data has childs containing a group property which contains the array. We need to fix that up, which we will do by overriding normalize:
normalize(type, hash, prop) {
hash.childs= hash.childs.group;
return this._super(type, hash, prop);
}
});
There are other issues to worry about, including whether you want Ember Data to serialize the embedded children (when writing back to the server), but the above should be enough to get you started.
By the way, I notice you have two different groups with an id of 0. That is going to greatly confuse Ember Data. Try to have unique IDs. If that is not possible, you might consider synthesizing IDs with additional serializer logic.

How to represent complex objects in Ember models?

This SO question explains how to store an array in an Ember model, but how could it be done with a custom object, which also isn't supported natively supported, according to the Guides.
This is an example of the object that I am building
obj[0] = {"timeInMinutes": ">120", "occurrences": 24 };
obj[1] = {"timeInMinutes": "90-120","occurrences": 69 };
obj[2] = {"timeInMinutes": "60-90", "occurrences": 53 };
obj[3] = {"timeInMinutes": "30-60", "occurrences": 35 };
obj[4] = {"timeInMinutes": "0-30", "occurrences": 24 };
Update.
Using the information provided in this answer, I was able to create an array attribute on my model along with several other values, but I also want to be able to create DS.attr('object') attribute to be used like this. To create an object type, do I need to use a DS.Transform.extend({ as was done with the array in the linked to SO answer?
App.Index = DS.Model.extend({
names: DS.attr('array'),
country: DS.attr('string'),
statistics: DS.attr('object')
If you want the property to be a primitive object and not an Ember Object then you can do the following:
ObjectTransform = DS.Transform.extend({
deserialize: function(serialized) { return serialized; },
serialize: function(deserialized) { return deserialized; }
});
This is assuming that you don't need to change the object at all after it comes in over the wire.
If you’re using Ember Data, you’ll have to structure your models in an Ember Data-compatible way. You could have a parent Object (surely has a better name, but you didn’t explain your domain at all) that hasMany TimespanOccurrences or the like.

Ember Data: Automatically set belongsTo association

I am using Ember Data 1.0 (beta 1) and am somewhat confused by the default behavior of associations in ED.
Assume a Book model that has many (hasMany) chapters with each chapter belonging to (belongsTo) a book. My expectation was that the instances of a book's chapters property would automatically have a reference to the book instance through their belongsTo association, but this appears not to be the case. Is this indeed the default behavior in Ember Data or am I overlooking something?
If this is indeed the default behavior, does this mean that I need to create a custom serializer to accomplish this?
No ember-data will not do this for you but it should be possible to achieve. In both cases ember-data will sideload those properties. (In past versions you could setup a mapping so those would be embedded, but you no longer can do this) In your example you would have the following:
App.Book = DS.Model.extend({
chapters: DS.hasMany('Chapter')
});
App.Chapter= DS.Model.extend({
book: DS.belongsTo('Book')
});
Once those are setup ember-data by default will look for a data structured like this:
{
"book": {
"id": "1"
"chapters": ["1", "2", "3"]
},
"chapters": [
{
"id": "1",
"book": "1"
},
{
"id": "2",
"book": "1"
},
{
"id": "3",
"book": "1"
}
]
}
If your data is not in that format and you can not change it then you can extend the extractSingle or extractArray methods on the serializer for that type. At the bottom of this link you can find some more info on that. Also remeber it looks for it in camelcase so you may also need to normalize the json object as well.

Generic objects, concrete routes / models / controllers / views / templates

My backed has a list of objects, at api/vehicles:
{
"vehicles" : [
{
"id" : "car" ,
"nr-doors" : 4,
...
},
{
"id" : "ship",
"nr-sails" : 3
...
},
{
"id" : "train",
"seats-per-wagon" : 30,
...
}
]
}
All vehicles have only the "id" field in common, and they have several specific properties. There is only one vehicle of each kind. The IDs are fixed and already known. I would like to have a template to display/edit the car's properties. How can I relate a specific id (like "car") with a model/template/controller/view?
The problem that I have is that in ember and ember-data, the IDs at a certain URL refer to objects of the same kind, which can be handled by the same route/view/controller/model. In my application, this is not the case: each ID must be handled in a completely different controller/model/view/template.
How can I do this?
each ID must be handled in a completely different controller/model/view/template
Are you sure? This would be a radical departure from not just ember but any model-view-controller architecture.
If you really want to accomplish this, I would suggest the following:
Start by creating the templates and routes you want to have
App.Router.map(function() {
this.route("car");
this.route("ship");
this.route("train");
});
App.CarRoute = Ember.Route.extend({
model: function() {
return App.Vehicle.find('car');
}
});
App.CarController = Ember.ObjectController.extend({
//add any custom fx related to cars here
});
// Now define handlebars template for car and if needed a CarView

Define a complex model

I have the following user object (json):
{
"_id": "my-id",
"org": 666,
"type": "user",
"properties": {
"first_name": "Firstname",
"surname1": "Surname1",
"surname2": "Surname2",
"allowed_apps": [ "one-app", "another-app" ],
"default_app": "one-app",
"email": "email#address.com"
},
"outputs": {
"extension": "extension-id"
}
}
This is a single model, with a complex structure. It is not a "multi-model structure". Therefore, I want to define this using DS.Model.extend, but I do not want to use belongsTo relationships for neither properties nor outputs, since they do not refer to other models: these fields are just complex data, direct part of my user object. That is, we do not have any properties model, nor any outputs model. These are just parts of the user model, and as such are stored in the database (couchdb in our case).
Can this be done in ember?
Lamentably this kind of structure is not possible using ember-data models.
Every value of a model that is not a primitive one such as string, number, boolean & date can't be defined as a model property without designing it with belongsTo or hasMany. Furthermore this site jsonapi.org which is still a WIP describes the direction ember-data is going with it's implementation.
So the point is here if you want/need to use ember-data models (DS.Model) your server should obey the JSON format ember-data expects, otherwise you have always the possibility (since ember-data is backend agnostic) to not use ember-data models definition at all, this way your models can be structured the way you want, but then you are out of being helped from the conventional work ember-data adapter and serializer does for you and you have to write your own adapter/s which then deals with all the peculiarities your JSON's have and load them finally into your store.
If you absolutely need a custom data structure to be exchanged with your backend, you can register a custom transform like for example:
App.Adapter.registerTransform('mega', {
deserialize: function(serialized) {
//do your custom deserialization
},
serialize: function(deserialized) {
//do your custom serialization
}
});
and then use it like:
App.MyModel = DS.Model.extend({
megaValue: DS.attr('mega');
});
Hope it helps.