Ember Data: option to not use identity map and ignore relationship - ember.js

One of the powers of Ember Data is that it uses identity maps to ensure that there is a single source of truth, but to ensure that this happens, it's very prescriptive on how the models need to be configured and what the server needs to return. But sometimes in a mostly read-only app, the single source of truth is not necessary and changing the API on the server is more work than necessary. In these instances, is there a way to tell Ember Data to just accept an array or object as is and don't try to map it to an identity map? Here's a quick example using widget and category:
This is what Ember Data wants us to do:
App.Widget = DS.Model.extend({
name: DS.attr('string'),
categories: DS.hasMany('category')
});
App.Category = DS.Model.extend({
name: DS.attr('string')
});
And Ember Data wants us to return this from the server:
{
"widget": {
"name":"Awesome Device!",
"categories":[1,2]
},
"categories":[
{id:1,"name":"Device"},
{id:2,"name":"Fun"}
]
}
But if this is a mostly a read-only app where the categories won't change, is there a way to tell Ember Data to treat categories as just simple value objects?
App.Widget = DS.Model.extend({
name: DS.attr('string'),
categories: DS.attr('array') // any way to do something like this?
});
Then the server can simply return this:
{
"widget": {
"name":"Awesome Device!",
"categories": [
{ id: 1, "name": "Device" },
{ id: 2, "name": "Fun" }
]
}
}

You can create your own transform, some common ones that you might attempt are raw, or array. I generally gravitate toward raw when I'm lazy
App.RawTransform = DS.Transform.extend({
deserialize: function(serialized) {
return serialized;
},
serialize: function(deserialized) {
return deserialized;
}
});
App.register("transform:raw", App.RawTransform);
App.Widget = DS.Model.extend({
name: DS.attr('string'),
categories: DS.attr('raw')
});
http://emberjs.com/api/data/classes/DS.Transform.html

Related

How to use the FilterBy for multiple data in ember.js

That is controller chat.js code
queryParams: ['offer_id'],
offer_id: null,
filteredChat: Ember.computed('model.#each.offer_id','offer_id',
function() {
return this.get('model').filterBy("offer_id" ,this.get("offer_id")).filterBy("id", this.get("offer_id"))
}),
I am filtering the chat with offer_id.
I want to know that can i use the filterBy like this two times
and that is my route chat.js code
queryParams:{
offer_id:{
refreshModel : true
}
},
model(params) {
return this.store.query("chat", params).then(() => {
let model = this.store.peekAll("chat")
return model
})
},
My Model chat.js
message: attr('string'),
offer_id: attr('string'),
stamp: attr('string'),
type: attr('string'),
You can do this, but be careful, your dependency string is wrong. You should include model.#each.id as well.
Also you need to understand that this will result in an array with the items that both the offer_id and the id are the same and exactly the offer_id you'r filtering on. This will not result in the items that either the offer_id or the id are equivalent to you filter offer_id.
If you want the or version you could do this:
return [
...this.get('model').filterBy("offer_id" ,this.get("offer_id")),
...this.get('model').filterBy("id" ,this.get("offer_id")),
]
so you see, if this works depends absolutely on what you expect.

Emberdata How to make a request on a dynamic segment

This is my router:
Router.map(function() {
this.route('merchant', { path:'/merchant/:id' }, function() {
this.route('product-tag');
Currently my api works like this. So I'm trying to get all the product tags that belong to merchant with id: 1781.
http://localhost:3001/merchant/1781/product_tags
The closest I've gotten is using a the product-tag route doing something like this:
model: function() {
debugger;
var parentModel = this.modelFor('merchant').merchant;
return this.store.find('product-tag', { merchant_id: parentModel.id});
}
This will generate a request:
http://localhost:3000/product_tags?merchant_id=1781
I'd assume that because product_tag is a subroute of merchant it'd take into account the dynamic segment of merchant but that doesn't seem to be the case.
Thanks for the help.
My models are as follows:
Merchant:
export default DS.Model.extend({
user_id: DS.attr('number'),
tags: DS.hasMany('product-tag', {async: true})
});
product-tag:
export default DS.Model.extend({
merchant: DS.belongsTo('merchant', {async: true}),
name: DS.attr('string'),
active: DS.attr('boolean'),
taggings_count: DS.attr('number')
});
model hook has two arguments. The first one should content dynamic segments. So, something like this should work:
//Router
Router.map(function() {
this.route('merchant', { path:'/merchant/:merchant_id' }, function() {
this.route('product-tag');
//Route
model: function(params) {
return this.store.find('product-tag', { merchant_id: params.merchant_id});
}
As for the second part of your question, ember data doesn't support nested URLs. Discussion on this subject
Your application routes are unrelated to the API endpoints Ember Data will make requests to. Adapters will build the API request per model.
If you have control of the API server, the easiest way to retrieve merchant's product tags is to send a link with your merchant payload. I don't know what format your API uses, but should be something like:
"merchant": {
"id": "1",
"user_id": "10"
"links": {
"product-tags": "http://localhost:3001/merchant/1781/product_tags"
}
}

How to load hasMany in Ember?

I have two models:
App.Offer = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
products: DS.hasMany('product')
});
App.Product = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
offer: DS.belongsTo('offer')
});
And the server is answering with record and array of ids in this way (for example if the rest adapter asks for /offers/1):
{ "offer": [ { "id": 1, "name": "aaaaaaaaa", "description": "aaaa", "product_ids": [ 1, 2 ] } ] }
but now how can I get the products? I have a route like this:
App.OffersRoute = Ember.Route.extend({
model: function() {
var offer = this.get('store').find('offer', 1);
return offer
}
});
In Ember guide is written that if you want the products you should do:
offer.get('products');
Ok, but where should I put this? in the model hook? in a Controller property?
I've tried many things but I can see no network request to products?id[]=1&id[]=2 as I expected (the server is responding correctly to this request);
Can someone please give an example showing how I can find an offer, its products and use this data in my template?
If you're using the RESTAdapter your data needs to be in this format (if you don't want to return it in this format you can create a custom serializer and fix up the json).
2 differences:
the item under offer shouldn't be an array, since you were looking for a single item it should be an object
the key product_ids should be products, product_ids is the format that the ActiveModelAdapter/ActiveModelSerializer use.
JSON
{
"offer":
{
"id":1,
"name":"aaaaaaaaa",
"description":"aaaa",
"products":[
1,
2
]
}
}
The hasMany relationship should be marked as async if you're expecting it to be returned in a separate payload.
App.Offer = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
products: DS.hasMany('product', {async:true})
});
I hooked it up in jsbin below, but I didn't hook up a result from products?ids[]=1&ids[]=2 (note ids[]=, not id[]=), if you check the network tab you'll see the request being issued (but it'll crash since there is no result).
http://emberjs.jsbin.com/OxIDiVU/345/edit

Embedded hasMany testing with Ember

I'm trying to understand using Ember's hasMany. In particular, I'd like to be able to grab a particular object(s). I've tried grabbing firstObject on it but that doesn't work. And I've also tried looping over each object.
jsBin and jsBin with looping
Important code:
App.MyModel = DS.Model.extend({
name: DS.attr('string'),
myOthers: DS.hasMany('App.MyOtherModel')
});
DS.RESTAdapter.map('App.MyModel',{
myOthers: { embedded: 'always' }
});
App.MyOtherModel = DS.Model.extend({
name: DS.attr('string')
});
App.store.load(App.MyModel, {
id: 2,
name: "myModel",
my_others: [
{ name: 'myOther1' },
{ name: 'myOther1' }
]
});
console.log(myModel.get("myOthers.firstObject.name"));
I'm trying to do this for my tests, but I'm not having any luck.
How would I work with a hasMany relationship in order to grab a particular object and be able to loop over them? Thanks.
I found out that you have to go through the adapter, not the store.
updated jsbin
App.adapter = DS.RESTAdapter.create();
App.adapter.load(App.store, App.MyModel, {
id: 2,
name: "myModel",
my_others: [
{ name: 'myOther1' },
{ name: 'myOther2' }
]
});

How to fetch embedded objects in one request with ember-data and couchdb-adapter

I am working on an application using ember.js and a couch DB backend. So far, i used ember-resource as database driver, but I am considering switching to ember-data, since this seems to be more sustainable.
Since I am working with couch DB, I am using the Couch DB-Adapter.
The documents in my database contain complete object structures, so I have to specify embedded objects in the database driver.
But although I am specifying my sub-objects as embedded, ember-data seems to fetch these objects with separate requests, instead of just getting them out of the main json.
My object definitions are as follows:
App.UserProfile = DS.Model.extend({
type: DS.attr('string'),
fullname: DS.attr('string'),
email: DS.attr('string'),
pictureUrl: DS.attr('string'),
social: DS.hasMany('App.SocialWebAccount', { embedded: true }),
.....
});
App.SocialWebAccount = DS.Model.extend({
profile: DS.belongsTo('CaiMan.UserProfile'),
site: DS.attr('string'),
account: DS.attr('string'),
.....
});
and the server data ist something like this:
{
"_id": "thoherr",
"_rev": "55-d4abcb745b42fe61f1a2f3b31c461cce",
"type": "UserProfile",
"fullname": "Thomas Herrmann",
"email": "test#thoherr.de",
"pictureUrl": "",
"social": [
{
"site": "socialFacebook",
"account": "thoherr"
},
{
"site": "socialXing",
"account": "Thomas_Herrmann7"
},
{
"site": "socialEmail",
"account": "test#thoherr.de"
}
]
}
After loading, the UserProfile does contain an ArrayProxy for my social data, which is populated by three entries, but they are all undefined instead of instances of SocialWebAccount!
If i try to access this array, ember-data seems to do a separate database access to fetch the data, which then leads to an error because the couch DB-adapter accesses an _id field, which is not available in undefined....
What am i missing?
I thought the "embedded" flag signals that the data is already in the json and the objects can be instantiated from the json?
Why does ember-data try to fetch the embedded data?
Any hint?
It seems that the embedded option has changed recently. I found some information in the test files on the ember-data github.
In these test files, the embedded content is defined like this
Comment = App.Comment = DS.Model.extend({
title: attr('string'),
user: DS.belongsTo(User)
});
Post = App.Post = DS.Model.extend({
title: attr('string'),
comments: DS.hasMany(Comment)
});
Adapter = DS.RESTAdapter.extend();
Adapter.map(Comment, {
user: { embedded: 'always' }
});
or
Adapter = DS.RESTAdapter.extend();
Adapter.map(Comment, {
user: { embedded: 'load' }
});
'always' seems to be used for embedded data without ids (your case),eg
id: 1,
title: "Why not use a more lightweight solution?",
user: {
name: "mongodb_expert"
}
'load' seems to be used for embedded data with ids
id: 1,
user: {
id: 2,
name: "Yehuda Katz"
}
Hoping it will help in your particular case. I've had a lot of trouble with hasMany relationships recently (I had to modify my adapter to get it work)