I have a very common situation, if there is a term for it, well I am not aware of it then.
A record is having fields: id, enabled, text, etc...
and having POST /record/enable to enable or disable record, as it invoke is bigger process on server.
So, once callback from normal POST is received, I want to update record.enabled to true locally, which should not be part of any transaction. and should be updated directly.
How to achieve this?? Or what is better alternative for such requirement?
I think something along these lines should do:
App.PObject = DS.Model.extend({
id: DS.attr('number'),
name: DS.attr('string'),
nestedObject: function() {
if (!this.nestedObj) {
this.nestedObj = Ember.Object.create({
active: false
});
}
return this.nestedObj
}.property()
});
As i have seen so far, Model is dirty only when its attributes, that are defined as DS.attr, change.
EDIT: Realized later that, This solution doesn't work.
Proper term of my problem is: transient field
Transient field of an Object is ignored for serialization/deserialization (like Java ORMs ignores transient fields) http://en.wikipedia.org/wiki/Transient_(computer_programming
I have figured out a solution. Its really simple.
App.PObject = DS.Model.extend({
id: DS.attr('number'),
name: DS.attr('string'),
//will not be part of Ember-data persistence, but will be populated on GET
enabled: null
});
Object (Record) properties which are not attributes are ignored.
Related
I had this code in my ember app:
var FooModel = DS.Model.extend({
bars: DS.hasMany( 'bar', { async: true, inverse: 'foo'} )
});
var BarModel = DS.Model.extend({
foo: DS.belongsTo( 'foo', { async: true, inverse: 'bars'} )
});
Edit: Using
Ember : 1.13.7
Ember Data : 1.13.8
But when I went to render foo.bars, they wouldn't be loaded unless I used the browser back and forward buttons. Reloading the page would cause the foo.bars to disappear again.
When I changed the code to this:
var FooModel = DS.Model.extend({
bars: DS.hasMany( 'bar', { async: true } )
});
var BarModel = DS.Model.extend({
});
Everything works as it should, and I'm just really confused as to why. Especially since I took that original code from another ember app where it was working just fine (although there might have been some adapter/serializer magic going on that I don't know about). Edit: The app where it was working is using
Ember : 1.4.0
Ember Data : 1.0.0-beta.7+canary.b45e23ba
Handlebars : 1.3.0
Edit: Using REST adapter for both
Async in relatioships
{ async: true} is the default value for relationships in Ember since 1.13 which means that in most cases you define the relationship without specifying the async value at all. It means that related records will not be loaded into the store until required. This is almost always the desired way to go since it prevents blocking by returning a promise. Remember that promises require a different way of programming since the process no longer is procedural step by step through the code. You manage promises by chaining a .then(function(param){// handle fulfilled promise here});.
There are cases where { async: false} could be beneficial. For example in a one to one relationship and you wanted both sides of the relationship to be loaded immediately.
Why your code does not work
I don't know for sure but it seems that the code as you have written it and it could be a bug in the code. The same as with async above you also do not actually need to specify the inverse value here either. According to Ember documentation:
Ember Data will do its best to discover which relationships map to one another. Explicit Inverses
Your application as a simple one to many relationship should work fine simply with:
var FooModel = DS.Model.extend({
bars: DS.hasMany( 'bar' )
});
var BarModel = DS.Model.extend({
foo: DS.belongsTo( 'foo' )
});
I have replied a similar question What is an “async relationship”? / {async: true} vs. {async: false}
Async relationships (default)
Accessing the relationship will return a promise.
post.get('comments').then((comments) => {
// now we can work with the comments
});
If the data is not available and the relationship is accessed (for example, from a template or a "consumed" computed property), Ember Data will automatically fetch the resources.
Read more about Relationships as Promises
Sync relationships (from docs)
Ember Data resolves sync relationships with the related resources
available in its local store, hence it is expected these resources
to be loaded before or along-side the primary resource.
BelongsTo
export default DS.Model.extend({
post: DS.belongsTo('post', {
async: false
})
});
In contrast to async relationship, accessing a sync relationship
will always return the record (Model instance) for the existing
local resource, or null. But it will error on access when
a related resource is known to exist and it has not been loaded.
let post = comment.get('post');
HasMany
export default DS.Model.extend({
comments: DS.hasMany('comment', {
async: false
})
});
In contrast to async relationship, accessing a sync relationship
will always return a DS.ManyArray instance
containing the existing local resources. But it will error on access
when any of the known related resources have not been loaded.
post.get('comments').forEach((comment) => {
});
If you are using links with sync relationships, you have to use
ref.reload to fetch the resources.
Is there a conventional way to look up a singleton in Ember Data? I've set up my adapter for my GameState model from these instructions (under "Singular"): https://stackoverflow.com/a/23974804/173630
Now, when I fetch the GameState I'd like to be able to do this:
store.find('gameState');
But that initiates a findAll request type in Ember Data, which expects an array, not an object.
Another option I have is to do something like this:
store.find('gameState', 'mine');
The mine param is ignored, but this will successfully fetch and load my GameState. The downside here is that Ember Data creates an extra GameState instance in my store with an id of mine. Is there any way to prevent this?
I'm using Ember Data 1.0.0-beta.11.
Here's what my model looks like:
var GameState = DS.Model.extend({
currentPuzzle: DS.belongsTo('puzzle'),
currentRound: DS.belongsTo('puzzleRound'),
currentInventory: DS.attr('number'),
moves: DS.hasMany('move', {async: true}),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date')
});
No, unfortunately that's a side affect of doing it like this. You can do a few different things here:
Hack it out using private methods/members
Switch to a findAll/findQuery approach changing your api and then only returning the first item from the results (which would be the only result).
Make a boring ajax call, and sideload the record.
findAll/findQuery - pain cause you have to change your api
return this.store.find('foo').then(function(records){
return records.get('firstObject');
});
jquery ajax sideload
var store = this.store;
return $.ajax('foo').then(function(data){
return store.push('foo', data); // push returns the record
});
http://emberjs.com/api/data/classes/DS.Store.html#method_push
This is really confusing me at the moment and it's probably dead simple to fix.
I have two model, related between themselves:
App.User = DS.Model.extend({
name: DS.attr('string'),
zip_code: DS.attr('string'),
address_line: DS.attr('string'),
country: DS.attr('string'),
active: DS.attr('boolean'),
state: DS.attr('string'),
comments: DS.attr('string'),
user_type: DS.belongsTo('user_type', { async: true })
});
App.UserType = DS.Model.extend({
name: DS.attr('string'),
users: DS.hasMany('users', { async: true })
});
I'm trying to the a User's UserType.name:
Simple enough right? Wrong!
// Doesn't work.
user.get('user_type.name');
// Also doesn't work.
user.get('user_type').get('name');
In my console, I can clearly see the object is there and related.
You will probably need to "resolve" the UserType:
user.get('user_type').then(function(userType) {
console.log("User type is:", userType.get('name'));
});
Note that this introduces asynchrony to your method... Depending on what you're trying to do, you'll need to accomodate for that. Normally, when bound to a Handlebars template, this resolving of related types happens automatically, so you should be able to (in a template) use {{user.user_type.name}}
Because you've set the relationship as {async: true}, Ember presumes that the relationship is asynchronous, i.e. it needs to load the UserType model separately. So, at least the first time it's called, user.get('user_type') actually returns a promise which, at the point at which the chained method is called, is not guaranteed to have loaded content.
Even if it's already in the store, (if memory serves) Ember is not going to attempt to set it until the next run loop. Calling .get('name') on that promise immediately will thus give you null.
The user.get('user_type.name') syntax will just resolve internally to the same thing as user.get('user_type').get('name') so that result will be no different.
Steve H.'s response should work. Alternatively, if you're working with a server (not fixtures) and the server response is such that the userType is sideloaded with the user – or if you are in a position to change the response – you should be able to remove the {async: true} conditions, and both of the expressions in your question should work as expected.
Does anyone know of a way to specify for an Ember model an attribute which is not persisted?
Basically, we're loading some metadata related to each model and sending that data to Ember via the RESTAdapter within the model. This metadata can be changed in our app, but is done via using an AJAX call. Once the call succeeds, I want to be able to update this value within the model without Ember sticking its nose in this business by changing the model to the uncommitted and doing whatever it does with transactions behind the scenes.
I also have the problem that this metadata, which is not data from the model's database record, is passed by the RESTAdapter back to the server, which doesn't expect these values. I am using a RoR backend, so the server errors out trying to mass-assign protected attributes which aren't meant to be attributes at all. I know I can scrub the data received on the server, but I would prefer the client to be able to distinguish between persistent data and auxiliary data.
So, to the original question: is there any alternative to Ember-Data's DS.attr('...') which will specify a non-persistent attribute?
The other answers to this question work with Ember data versions up to 0.13, and no longer work.
For Ember data 1.0 beta 3 you can do:
App.ApplicationSerializer = DS.RESTSerializer.extend({
serializeAttribute: function(record, json, key, attribute) {
if (attribute.options.transient) {
return;
}
return this._super(record, json, key, attribute);
}
});
Now you can use transient attributes:
App.User = DS.Model.extend({
name: DS.attr('string', {transient: true})
});
These attributes won't be sent to the server when saving records.
When this PR get's merged it will be possible to flag properties as readOnly. But till then there are some workarounds to this, e.g. overriding your addAttributes method in the Adapter and deal with your special properties, here an example how this could look like:
Define your Model by adding the new option readOnly:
App.MyModel = DS.Model.extend({
myMetaProperty: DS.attr('metaProperty', {readOnly: true})
});
and then on the Adapter:
App.Serializer = DS.RESTSerializer.extend({
addAttributes: function(data, record) {
record.eachAttribute(function(name, attribute) {
if (!attribute.options.readOnly) {
this._addAttribute(data, record, name, attribute.type);
}
}, this);
}
});
what this does is to loop over the attributes of your model and when it find's an attribute with the readOnly flag set it skips the property.
I hope this mechanism works for your use case.
Following this answer, to prevent a field from being serialized, override the default serializer for your model:
In app/serializers/person.js:
export default DS.JSONSerializer.extend({
attrs: {
admin: { serialize: false }
}
});
See here for the source PR. This solution works in Ember Data 2, and should work in older versions as well.
Update
This answer is most likely out of date with the current releases of Ember Data. I wouldn't use anything in my answer.
I'm answering this question for reference, and because your comment indicated that the record remains isDirty, but here is my solution for read-only, non-persistent, non-dirty attributes.
Overriding the addAtributes method in your Serializer prevents readOnly attributes from being sent to the server, which is probably exactly what you want, but you need to extend (or reopen) your adapter to override the dirtyRecordsForAttributeChange to prevent the record from becoming dirty:
App.CustomAdapter = DS.RESTAdapter.extend({
dirtyRecordsForAttributeChange: function(dirtySet, record, attrName, newValue, oldValue) {
meta = record.constructor.metaForProperty(attrName);
if (meta && meta.options.readOnly) { return; }
this._super.apply(this, arguments);
};
});
Then you can use readOnly attributes like so:
App.User = DS.Model.extend({
name: DS.attr('string', {readOnly: true})
});
user = App.User.find(1); # => {id: 1, name: 'John Doe'}
user.set('name', 'Jane Doe'); #
user.get('isDirty') # => false
This setup is working for me.
I'm new to ember, and try to understand how it works.
I've defined a store with a fixturesAdapter as adapter (rev 7).
I've defined two models:
App.Tag = DS.Model.extend({
name: DS.attr('string'),
item: DS.belongsTo('App.Item')
});
and:
App.Item = DS.Model.extend({
name: DS.attr('string'),
tags: DS.hasMany(App.Tag, { embedded:true }),
})
I also fill their associated fixtures and at last a controller:
App.itemsController = Ember.ArrayController.create({
content: App.store.findAll(App.Item)
});
I've defined a function inside App.Item model:
tagline: function(){
return this.get('tags').toArray().map(function(tag){
return tag.get('name');
}).join(',');
}.property('tags.#each.isLoaded')
Here is the corresponding jsfiddle: http://jsfiddle.net/K286Q/29/
My questions are:
What am I doing wrong?
Why does it see several tags associated to first item, but is not able to get their name?
You're running up against a few breaking changes in the current version of ember-data.
The first is that, since revision 6 of ember-data, IDs are string-normalized and must be represented as strings in fixtures. Note that the REST adapter will convert numbers/strings, but the fixture adapter doesn't do any conversions. This is a common source of confusion (see the previous question).
The second is that support for embedded data objects has been temporarily removed from ember-data. I'm pretty sure that this feature will be re-introduced in a better way than supporting {embedded: true} in the attributes. IMO, embedding is more of an adapter concern and doesn't really belong with the definition of the model.
I adjusted your fixtures and got your example working here: http://jsfiddle.net/dgeb/zHz4Y/