I'm writing a custom data adapter (doesn't talk to webserver) and I need to know whether hasMany relationship was changed or not to persist relationship data.
App.Note = DS.Model.extend({
content: attr('string'),
createdAt: attr('date'),
tags: DS.hasMany('tag', { async: true })
});
App.Tag = DS.Model.extend({
name: DS.attr('string'),
notes: DS.hasMany('note')
});
I need to implement adapter's updateRecord:
updateRecord: function(store, type, record) {
// record.changedAttributes() returns changed attributes but not relationships
}
In my case list of tags attached to note may change (via note.get('tags').addObject(..) / removeObject(..)) so I need to get a diff. What's the best way to do that? I can remove all tags in a database and insert new tags but that's not efficient at all
Related
I have a basic order entry form with billing and shipping address fields (both based on an 'address' model). I have a checkbox that says "Billing address same as shipping?" that, when checked, will copy the billing address data to the shipping address.
How would I do this? It's not quite apparent to me. I'm thinking that when the "next" button is clicked, if the "billShipSame" value = true then copy the data. But how do you actually copy the data? Or am I just approaching this problem wrong?
The model looks like:
export default DS.Model.extend(Validations, {
type: attr('string'),
firstName: attr('string'),
lastName: attr('string'),
address1: attr('string'),
address2: attr('string'),
city: attr('string'),
state: attr('string'),
country: attr('string'),
postalCode: attr('string'),
phone: attr('string')
});
And here's a stripped-down version of how I'm using them:
billingAddress: computed(function() {
return this.get('store').createRecord('address', { type: 'billing'});
}),
shippingAddress: computed(function() {
return this.get('store').createRecord('address', { type: 'shipping'});
}),
orderModel: computed(function() {
return this.get('store').createRecord('order', {
billingAddress: this.get('billingAddress'),
shippingAddress: this.get('shippingAddress')
});
}),
I would suggest having you "same as billing" radio button trigger an action that copies the data into the appropriate fields. That way by the time someone clicks next, your data model is in good shape and your submit action can focus on saving
Edit:
These easiest way to copy values between two models is as follows:
shippingAddress.setProperties(billingAddress.getProperties('firstName','lastName')); // etc
Believe that should handle what you're after ...
Ex:
I have model/test.js
export default Model.extend({
name: attr('string'),
email: attr('string'),
address: attr('string'),
age: attr(),
phone: attr()
});
In component.js
list: computed('model.{}', function() { });
Fetching data in route and passing it to the template. In child component i am trying to access it. initially data passed will be like
{
'data': {
name: 'test'
}
}
later sending data as
{
'data': {
name: 'test',
email: 'test#gmail.com',
address: 'XYZ',
age: 10,
phone: 423423
}
}
But in computed property it is not listening second updated data. I want to listen dynamically each property in model. It will work if i give like
list: computed('model.{name,email,address,age,phone}', function() { });
But i want some other solution for it. Dynamically need to listen each property in model object.
If you are dealing single object, then what you have is the right and only possible way to go.
list: computed('model.{name,email,address,age,phone}', function() { });
Suppose if your model is array of object then you can do the below things,
list: computed('model.#each.{name,email,address,age,phone}', function() { });
You can try using #each in the computed property.
list: computed('model.#each', function(){});
It will work for all properties inside the model class. But it works only one level deep.
I'm having some trouble getting hasMany relationships to auto load (default or async) - I'm using the "links" attribute so i can use a custom url for children, and using a custom serializer to put the links attribute in since the server doesn't provide it - is this not supported? (using ember data 1 beta 6 and ember 1.3.2)
App.Bag = DS.Model.extend({
elements: DS.hasMany('element')
});
App.Element = DS.Model.extend({
name: DS.attr('string')
});
App.BagSerializer = DS.RESTSerializer.extend({
extractSingle: function(store, type, payload, id, requestType) {
payload.links = {"elements": "/bags/" + id + "/elements"};
return this._super(store, type, payload, id, requestType);
}
});
I'm able to load a Bag fine, but the elements array is never populated, I never see a call to the /bags/id/elements url. Am I doing something wrong?
Thanks!
How about if you specify the elements relationship is asyc? Like this:
App.Bag = DS.Model.extend({
elements: DS.hasMany('element', {async: true})
});
My model is defined as follow:
App.User = DS.Model.extend({
primaryKey: 'username',
username: DS.attr('string'),
name: DS.attr('string')
});
My custom Adapter map:
DS.SocketAdapter.map('App.User', {
primaryKey: 'username',
username: DS.attr('string'),
});
I am testing this model out by typing on console:
App.User.createRecord({username:"user_1"});
var r = App.User.find("user_1");
console.log( r.serialize() );
>> Object {username: null, name: null ..... all null}
But it retuns a "null" Object. Also tested:
App.User.find({username:"user_1"});
But this is doing a remote request. I read that Ember Data does allow you to find records via attributes other than the ID.
So what I am doing wrong in telling Ember data my custom primaryKey?
I think your problem lies in the fact that you are defining username twice. if you map username from your json to your model's primaryKey trough your Adapter, then you should avoid to do the same on the model I guess. There are different approaches where to define the mapping, but the Adapter is the most appropriate place In your case, see here for more details: https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md#mapping
change your code like so and it should work:
// Model
App.User = DS.Model.extend({
name: DS.attr('string')
});
// Adapter
DS.SocketAdapter.map('App.User', {
primaryKey: 'username'
});
now try to create a new record
App.User.createRecord({username:"user_1", name:"foo"});
and then find the record by it's id as you already did:
var r = App.User.find("user_1");
this
console.log( r.serialize() );
should then give you at least:
>> Object {name: "foo" ...}
hope it helps
Has anybody come up with an answer for polymorphic associations and ember-data?
We would need some way of being able to query the type at the other end of the relationship from what I can tell.
Anybody any thoughts on this?
With the latest ember-data build you can now use polymorphic associations:
You need to configure your Models to make it polymorphic:
/* polymorphic hasMany */
App.User = DS.Model.extend({
messages: DS.hasMany(App.Message, {polymorphic: true})
});
App.Message = DS.Model.extend({
created_at: DS.attr('date'),
user: DS.belongsTo(App.User)
});
App.Post = App.Message.extend({
title: DS.attr('string')
});
/* polymorphic belongsTo */
App.Comment = App.Message.extend({
body: DS.attr('string'),
message: DS.belongsTo(App.Message, {polymorphic: true})
});
You also need to configure alias properties on your RESTAdapter
DS.RESTAdapter.configure('App.Post' {
alias: 'post'
});
DS.RESTAdapter.configure('App.Comment' {
alias: 'comment'
});
The result expected from your server should be like this:
{
user: {
id: 3,
// For a polymorphic hasMany
messages: [
{id: 1, type: "post"},
{id: 1, type: "comment"}
]
},
comment: {
id: 1,
// For a polymorphic belongsTo
message_id: 1,
message_type: "post"
}
}
More information in this github thread
So I have something. It's not finished, or entirely clean, but it works. Basically, I use a mixin to bypass the Ember associations entirely. I'm sure that this could be rolled into the adapter or the store, but for now this works.
Polymorphic models come through the the JSON with an itemId and itemType:
App.Follow = DS.Model.extend
user: DS.belongsTo('App.User')
itemId: DS.attr("number")
itemType: DS.attr("string")
I add a mixin to the models that are associated with it :
App.Hashtag = DS.Model.extend App.Polymorphicable,
follows:(->
name: DS.attr("string")
#polymorphicFilter(App.Follow, "Hashtag")
).property('changeCount') #changeCount gives us something to bind to
followers: (->
#get('follows').map((item)->item.get('user'))
).property('follows')
The mixin implements three methods, one that updates the changeCount, one that returns the model's type and the polymorphicFilter method that filters a model by itemType and id:
App.Polymorphicable = Ember.Mixin.create
changeCount: 1
polymorphicFilter: (model, itemType)->
App.store.filter model,
(data) =>
if data.get('itemId')
#get('id') is data.get('itemId').toString() and data.get('itemType') is itemType
itemType:()->
#constructor.toString().split('.')[1]
updatePolymorphicRelationships:()->
#incrementProperty('changeCount')
The controller layer is protected from all this jankyness, except for having to call updatePolymorphicRelationship to make sure the bindings fire:
App.HashtagController = Ember.ObjectController.extend
follow:()->
App.Follow.createRecord({
user: #get('currentUserController.content')
itemId: #get('id')
itemType: #get('content').itemType()
})
#this provides a way to bind and update. Could be refactored into a didSave()
#callback on the polymorphic model.
#get('content').updatePolymorphicRelationships()
App.store.commit()
That's what I have so far. I'm trying to keep things in the model layer as it's just one step removed from the adapter layer. If it looks like Ember Data is not going to look at polymorphics at all in future, then it would make sense to pull this all up to a higher level, but for now, this works and leaves my controllers (relatively) clean.
Polymorphic associations are now supported in ember data
https://github.com/emberjs/data/commit/e4f7c3707217c6ccc0453deee9ecb34bd65c28b9