Does ember.js still support embedded relationships? [duplicate] - javascript

I am really stuck with tons of problems caused by Ember-data and it lacks of embedded records support.
I have searched the entire web, most of the posts are outdated others are outdated + requires me to use 3rd party libraries or to wire up 300 lines of special code with lots of drawbacks.
I've no idea how to use embedded records with ember-data as it stands today?
edit: there is a new documentation now http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html

Using the ActiveModelSerializer you can include the EmbeddedRecordsMixin which allows you to use embedded records. (In the canary versions, 1.0 beta 9+, you can use the JsonSerializer/RESTSerializer as well)
Serializer
App.ColorSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
foos: {embedded: 'always'}
}
});
Models
App.Color = DS.Model.extend({
color: DS.attr(),
foos: DS.hasMany('foo')
});
App.Foo = DS.Model.extend({
name: DS.attr()
});
JSON
{
colors:[
{
id: 1,
color: "red",
foos:[
{
id:1,
name:'something 1'
},
{
id:2,
name:'something 2'
}
]
},
...
http://emberjs.jsbin.com/qagalabaso/1/edit
For the RESTSerializer and JsonSerializer it follows the same pattern
App.ColorSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
foos: {embedded: 'always'}
}
});
http://emberjs.jsbin.com/lesiwebobi/1/edit

Related

Duplicating nested models on ember.js (after successfull save)

I have model campaign, which has nested models stories and domains.
App.Campaign = DS.Model.extend({
name: DS.attr('string'),
stories: DS.hasMany('story'),
domains: DS.hasMany('domain')
});
App.Domain = DS.Model.extend({
domain: DS.attr('string'),
campaigns: DS.hasMany('campaign')
});
App.Story = DS.Model.extend({
title: DS.attr('string'),
campaign: DS.belongsTo('campaign')
});
In controller I create currentCampaign in this way:
this.currentCampaign = this.store.createRecord('campaign');
And add stories and domains in this way:
var campaignDomains = this.get('currentCampaign.domains');
var domainObj = this.store.createRecord('domain', {
"domain": domain
});
campaignDomains.addObject(domainObj);
var campaignStories = this.get('currentCampaign.stories');
var story = this.store.createRecord('story', {
"id": this.generateUUID(), // Generate uniqid
"title": storyTitle,
});
campaignStories.pushObject(story);
After saving (before calling transitionTo method I see duplicating domains and stories on template). When I go to page editing campaign I also see duplicating domains and stories. AFter refresh page - I see normal data (by refresh this data are fetching from server).
POST request JSON (domain id are generating on server side, story id are generating on client side):
{"campaign":
{
"id":"f0777f1a17deadcb",
"name":"name of campaign",
"stories":[{
"id":"488b6b6bf4c988f0",
"title":"story",
"campaign":"f0777f1a17deadcb"
}],
"domains":[{
"domain":"domain.com",
"campaigns":["f0777f1a17deadcb"]}
}]
}
Response on POST request (by adding new campaign):
{"campaign":
{
"id":"f0777f1a17deadcb",
"name":"name of campaign",
"stories":[{
"id":"488b6b6bf4c988f0",
"title":"story",
"campaign":"f0777f1a17deadcb"
}],
"domains":[{
"id":"54",
"domain":"domain.com",
"campaigns":["f0777f1a17deadcb"]}
}]
}
Serializer file
App.CampaignSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
domains: {embedded: 'always'},
stories: {embedded: 'always'}
}
});
Ember 1.8.1
Ember-data 1.0.0-beta.14.1
I think you have run into a issue I reported.
I had to downgrade to ember-data 1.0.0-beta.12
About stories (id of story are generating on client side) I used this.store.push
instead of this.store.createRecord - and this works fine.
About domains as I understood - this is bug ember.js. On github I found several discussions on this bug.
Now I use one hack to avoid duplicating data, I hope in future this bug will be fixed and I remove my hack.
var self = this;
this.get('currentCampaign').save().then(function() {
self.get('currentCampaign.domains').toArray().forEach(function(domain) {
if (domain.get('isNew')) {
domain.deleteRecord();
}
});
self.transitionToRoute('campaigns');
})

How to get result with specific fields in StrongLoop?

I am currently using StrongLoop as my API backend server and Mongodb as data storage engine.
Let's say there is a collection called article. It has two fields title, and content. And there are two frontend pages to display a list of articles and view a single article.
Obviously the data list page only need title field and the view page need both. Currently the GET method of StrongLoop API return all fields including content. It cost extra traffic. Is there any way that can just return specific field?
Mongodb support projection in find() method for this. How can I do the same thing by StrongLoop?
Have you taken a look at the filters offered. http://docs.strongloop.com/display/LB/Querying+models
Query for NodeAPI:
server.models.Student.findOne({where: {RFID: id},fields: {id: true,schoolId: true,classId: true}}, function (err, data) {
if (err)
callback(err);
else {
callback();
}
})
Query for RestAPI :
$http.get('http://localhost:3000/api/services?filter[fields][id]=true&filter[fields][make]=true&filter[fields][model]=true')
.then(function (response) {
}, function (error) {
});
You can use fields projections,
Sample Record:
{ name: 'Something', title: 'mr', description: 'some desc', patient: { name: 'Asvf', age: 20, address: { street: 1 }}}
First Level Projection:
model.find({ fields: { name: 1, description: 1, title: 0 } })
and I think Strong loop is not yet supporting for second-level object filter, does anyone know how to filter second-level object properties or is yet to implement?.
Second Level Projection: (Need help here)
Ex: 2
model.find({ fields: { name: 1, 'patient.name': 1, 'patient.age': 1, 'patient.address': 0 } })
// Which results { name } only

How to embed and write mongo objects in Sails.js (more than one level deep)?

From sails.js example,
// Person.js
var Person = {
attributes: {
firstName: 'STRING',
lastName: 'STRING',
age: {
type: 'INTEGER',
max: 150,
required: true
}
birthDate: 'DATE',
phoneNumber: {
type: 'STRING',
defaultsTo: '111-222-3333'
}
emailAddress: {
type: 'email', // Email type will get validated by the ORM
required: true
}
}
};
Now how would I add emailAddress to have a home and office as embedded fields?
Tried to do it this way:
emailAddress: { {
work: {
type: 'email',
},
personal: {
type: 'email',
}
}
},
and
emailAddress: {
attributes: {
work: {
type: 'email',
},
personal: {
type: 'email',
}
}
},
both don't work. I get errors such as "No Rules found for attributes" for the second case, "Unexpected token { " in the first case.
Ok, Following through some threads on this.
It seems Sails Waterline has no support for embedded MongoDB schema at this stage.
You can write your own contribution or force it, but the out of box support (model validation) etc. need to be hacked too.
https://github.com/balderdashy/sails-mongo/issues/44
The other option - sails-mongoose is unfortunately not supported too.
From where can we decide collection name in use of "sails-mongoose" package, in node.js + sailsjs?
Update.
Starting with V0.10, Sails is supporting associations. Perhaps that will make it work. Still experimental.
Update.
With the associations functionality you can force a schema in different models and create references between them, but so far it doesn't seem like you'll be able to embed them - only relate them from different collections/tables.
https://github.com/balderdashy/sails-mongo/issues/44
It seems they already have it planned as a Feature Request.

How to make embedded hasMany relationships work with ember data

I can't get embedded hasMany to work correctly with ember data.
I have something like this
App.Post = DS.Model.extend({
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.extend({
post: DS.hasMany('App.Post'),
name: attr('string')
});
And my API returns the following for GET /post:
[
{
id: 1
comments: [{name: 'test'}, {name: 'test2'}]
},
...
]
I need to send this with POST /post:
[
{
comments: [{name: 'test'}, {name: 'test2'}]
},
...
]
I want to work with Ember models and have them make the appropriate requests:
var post = App.store.createRecord(App.Post, hash_post_without_comments);
post.get('comments').createRecord(hash_comment);
App.store.commit(); // This should call the POST api
and
var posts = App.store.find(App.Post); // This should call the GET api
When I try something like post: DS.hasMany('App.Post', {embedded: true}), the GET is working but the POST is trying to make a POST for the two records not only the parent one.
EDIT : My Real use case
1- I've just built ember data from master
2- My adapter: RESTAdapter
3- The serializer: JSONSerializer
4- I added
App.MyAdapter.map('App.Join', {
columns: { embedded: 'always' }
});
5- My Models are:
App.Join = DS.Model.extend({
rowCount: DS.attr('number'),
columns: DS.hasMany('App.JoinColumn'),
});
App.JoinColumn = DS.Model.extend({
join: DS.belongsTo('App.Join')
});
6- When:
var a = App.Join.find(1);
a.get('columns').createRecord({});
App.store.commit();
a POST for joincolumn is sent and the parent is not dirty
What am i missing?
On master, the correct API is:
App.Adapter.map('App.Post', {
comments: { embedded: 'always' }
});
The two possible values of embedded are:
load: The child records are embedded when loading, but should be saved as standalone records. In order for this to work, the child records must have an ID.
always: The child records are embedded when loading, and are saved embedded in the same record. This, of course, affects the dirtiness of the records (if the child record changes, the adapter will mark the parent record as dirty).
If you don't have a custom adapter, you can call map directly on DS.RESTAdapter:
DS.RESTAdapter.map('App.Post', {
comments: { embedded: 'always' }
});
I have the exact same problem.
This bug has been reported on the ember data issue tracker.
The following PR adds 2 failing tests showing the problem: https://github.com/emberjs/data/pull/578
It seems that there is no workaround right now.
EDIT:
sebastianseilund opened a PR 2 days ago which fixes your problem.
Have a look at: https://github.com/emberjs/data/pull/629/files
Adding an update to this incase others come across this post and are having a hard time figuring out what works with the current version of ember-data.
As of Ember Data 1.0.0.beta.7, you need to override the appropriate methods on the serializer. Here's an example:
1) Reopen the serializer (credit to this post):
DS.RESTSerializer.reopen({
serializeHasMany: function(record, json, relationship) {
var hasManyRecords, key;
key = relationship.key;
hasManyRecords = Ember.get(record, key);
if (hasManyRecords && relationship.options.embedded === "always") {
json[key] = [];
hasManyRecords.forEach(function(item, index) {
// use includeId: true if you want the id of each model on the hasMany relationship
json[key].push(item.serialize({ includeId: true }));
});
} else {
this._super(record, json, relationship);
}
}
});
2) Add the embedded: 'always' option to the relationship on the model:
App.Post = DS.Model.extend({
comments: DS.hasMany('comment', {
embedded: 'always'
})
});
This is what worked for me (Ember 1.5.1+pre.5349ffcb, Ember Data 1.0.0-beta.7.f87cba88):
App.Post = DS.Model.extend({
comments: DS.hasMany('comment', { embedded: 'always' })
});
App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
comments: { embedded: 'always' }
}
});

Organizing sub-views with backbone.js?

I apologize for being new with backbone but I think I get the concepts.
For example: you have models for a Bookstore > Bookshelf > Book > Page
How would you organize this so the views can be controlled like this:
Bookstore.render() //to view the bookstore
Bookstore.bookshelf.get(shelfId).render() //to view shelf
Bookstore.bookshelf.get(shelfId).book.get(bookId).render() //to view book
Bookstore.bookshelf.get(shelfId).books.get(bookId).pages.at(0).render() //to view page
Is this the correct way to do it?
Yes It's possible and I created for you a working example, I think It's just an other method to organize your Backbone JS architecture and I think It's a pretty good idea.
Explications
All the models must have :
a render method which call the render method of its view. I create a base model that I extend for the Bookshelf, Book and Page model.
a initialize method, It create the collection (books for bookshelves, pages for book...)
Finally I instanced the Bookstore model and It's done !
Code
The demonstration : http://jsfiddle.net/Atinux/DvbA3/show/
Try in the console :
Bookstore.render()
Bookstore.bookshelves.get(1).render()
Bookstore.bookshelves.get(1).books.get(2).render()
Bookstore.bookshelves.get(1).books.get(2).pages.at(0).render()
The code with comments is available here : http://jsfiddle.net/Atinux/DvbA3/
I think the best thing is to understand how the code work. Feel free to ask me if you have problems to understand perfectly my code.
The JSON data
The JSON data have to look like :
var data = {
bookshelves: [{
id: 1,
name: 'Science',
books: [{
id: 1,
name: 'Abstract Algebra',
pages: [
{ content: 'Page 1 Abstract'},
{ content: 'Page 2 Abstract'},
{ content: 'Page 3 Abstract'}
]
}, {
id: 2,
name: 'Chemistry and Technology of Fertilizers',
pages: [
{ content: 'Chemistry page 1' },
{ content: 'Chemistry page 2' },
{ content: 'Chemistry page 3' }
]
}
]
}, {
id: 2,
name: 'Psychology',
books: [{
id: 1,
name: 'How to Think Straight About Psychology',
pages: [
{ content: 'Psychology page 1' },
{ content: 'Psychology page 2' },
]
}
]
}
]
};
#Alley, what you want to do goes against the MVC pattern since your model objects must be agnostic to the view.
#Atinux answer probably works but introduces a direct dependency between your view and model objects.
All presentational methods must reside in you view objects. Communication between view and models must be done using events.
So, instead of doing: Bookstore.bookshelf.get(shelfId).render()
You shall do something along this lines: bookshelfView.render()

Categories