I am developing a web site using Ember JS and Ember Data.
I am displaying a list of OrderItems, and each OrderItem has a belongsTo relationship to Product. In the object controller of the OrderItem, I need to calculate the line total (quantity * product price).
My issue is that I cannot get a hold of the product data.
Models
//OrderItem
export default DS.Model.extend({
order: DS.belongsTo('order'),
product: DS.belongsTo('product'),
quantity: DS.attr('number'),
lineTotal: DS.attr('number')
});
//Product
export default DS.Model.extend({
productNo: DS.attr('string'),
name: DS.attr('string'),
shortDescription: DS.attr('string'),
longDescription: DS.attr('string'),
price: DS.attr('number'),
stock: DS.attr('number'),
});
Controllers
//store/checkout-lines
export default Ember.ArrayController.extend({
itemController: 'store/checkout-line'
});
//store/checkout-line
export default Ember.ObjectController.extend({
orderlinetotal: function(){
var item = this.get('model');
var quantity = item.get('quantity');
var product = item.get('product');
var lineTotal = quantity * product.get('price');
return lineTotal;
}.property('quantity'),
actions: {
deleteItem: function(){
var item = this.get('model');
this.store.deleteRecord(item);
item.save();
}
}
});
This line:
var product = item.get('product');
returns an ember object
but this:
product.get('price');
returns undefined...
How can I solve this issue? I've tried to use .then(), like this:
product.then(function(){this.get('price');});
but then I get the error get(...).then is not a function, because product is not a promise but an Ember data object.
EDIT
This is the order model
export default DS.Model.extend({
customer: DS.belongsTo('customer'),
description: DS.attr('string'),
name: DS.attr('string'),
status: DS.attr('number'),
totalPrice: DS.attr('number'),
orderItems: DS.hasMany('order-item', {async: true})
});
This is the json responses (in the same order as they are executed)
//GET /orders/58
{"order":
{"id":58,
"description":null,
"name":null,
"status":2,
"totalPrice":5000.0000,
"orderItems":[36]}
}
//GET /orderItems?orderId=58
{"orderItem":[
{"id":36,
"product":2,
"quantity":1,
"order":58,
"lineTotal":5000.0000}
]}
//GET /product/2
{"product":
{"id":2,
"productNo":"0001-01",
"name": "Extreme Volume Mascara",
"shortDescription":"Sort",
"longDescription":"The best volumizing mascara there is",
"price":50.0000,
"stock":1000}
}
Edit 2
I found a solution to my problem. Not sure if this is the right way to do this, but I got the idea in a comment here at the ember discuss forum
I figured that the product data was loaded correctly, because I am displaying the properties in the store/checkout-lines template (and because I can see the HTTP request). But for some reason, in the store/chekout-line controller I could not get the data of the product, EXCEPT for the product id. Maybe the context was not properly loaded? I did how ever figure that it had something to do with the ObjectController (store/checkout-line), because if I loop throug all the orderItems in the ArrayController (store/checkout-lines) I have access to the product data.
Anyways here is my solution:
//store/checkout-line
export default Ember.ObjectController.extend({
isNew: function(){
return this.get('model').get('order').get('status') === 1;
}.property('order.status'),
orderlineTotal: function(k,v){
if(arguments.length > 1){
return v;
}
var self = this;
var model = this.get('model');
var productId = model.get('product').get('id'); //only property I have access to
var promise = this.store.find('product', productId); //this will not call the web api, because the product is already in memory
promise.then(function(product){
var result = product.get('price') * model.get('quantity');
self.set('orderlineTotal', result);
})
}.property('product.price', 'quantity'),
actions: {
deleteItem: function(){
var item = this.get('model');
this.store.deleteRecord(item);
item.save();
}
}
});
Related
I'm working with forms in Ember.js and I want to retrieve a list of all model properties so that I can take snapshots of the state of the form at different moments. Is there a way to get a list of all properties of a model?
For example, if my model is:
App.User = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
current_password: DS.attr('string'),
password: DS.attr('string'),
password_confirmation: DS.attr('string'),
admin: DS.attr('boolean'),
}
Then I would like to have something like this:
> getEmberProps('User')
["name", "email", "current_password", "password", "password_confirmation", "admin"]
You can simply use toJSON method on model and get the keys from object.
Ember.keys(model.toJSON())
Note that will not return you keys for relations.
You can also use this:
http://emberjs.com/api/data/classes/DS.Model.html#property_attributes
http://emberjs.com/api/data/classes/DS.Model.html#method_eachAttribute
Ember.get(App.User, 'attributes').map(function(name) { return name; });
Ember.get(userInstance.constructor, 'attributes').map(function(name) { return name; });
There's also similar properties for relationships too.
An easy way to print out the fields and their values:
Ember.keys(model.toJSON()).forEach(function(prop) { console.log(prop + " " + model.get(prop)); } )
There is no easy way, but you could try a custom mixin like this:
Ember.AllKeysMixin = Ember.Mixin.create({
getKeys: function() {
var v, ret = [];
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
} // ignore useless items
if (Ember.typeOf(v) === 'function') {
continue;
}
ret.push(key);
}
}
return ret
}
});
You can use it like this:
App.YourObject = Ember.Object.extend(Ember.AllKeysMixin, {
... //your stuff
});
var yourObject = App.YourObject.create({
foo : "fooValue";
});
var keys = yourObject.getKeys(); // should be ["foo"];
in 2018: use Ember Data's eachAttribute.
So, given a model Foo:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
"name": attr('string'),
"status": attr('string')
});
Let's get the model's definition via the constructor:
var record = this.store.createRecord('foo', {
name: "model1",
status: "status1"
});
this.modelClass = record.constructor;
and invoke a function for each of its Ember Data attributes:
modelClass.eachAttribute(function(key, meta){
//meta provides useful information about the attribute type
}
What i feel like I am doing is very basic yet I'm getting an undefined object everytime, I have an application called Example and have defined an ApplicationRoute:
Example.ApplicationRoute = Em.Route.extend({
model: function() {
var user = this.store.find('user',1);
console.log("username: " + user.username);
return { user: user };
}
});
And My Model:
var attr = DS.attr
Example.User = DS.Model.extend({
//logged in state can be loggedIn, loggedOff, pending, failed
loggedInState: attr('string'),
token: attr('string'),
username: attr('string'),
firstName: attr('string'),
lastName: attr('string'),
email: attr('string'),
isLoggedIn: Em.computed.match('loggedInState', /loggedIn/)
});
And lastly a FIXTURE:
Example.User.FIXTURES = [
{
id: 1,
username: 'jxnagl',
loggedInState: 'loggedOn',
firstName: 'Jared',
lastName: 'Nagle',
token: '1234',
email: 'jared.nagle#example.com'
}
]
It is my understanding that this FIXTURE will be present in the store by the time the model hook is called in the Application Route, But when i access my application, I get the following message in my console:
"username: undefined"
Is there something I'm missing? Ember's documentation shows similar examples and I don't see a difference.
"find" returns promise.
var user = this.store.find('user', 1).then(function(user){
console.log('username: ' + user.get('username'));
});
I think your Route should be like
Example.ApplicationRoute = Em.Route.extend({
model: function() {
return this.store.find('user',1);
}
});
I think the problem here was the ember-auth plugin I had a dependency on. It was somehow interferring my application. Removing this plugin solved the problem for me. :)
I have a blog application where the API calls the users "Users", but my Ember model is "Author".
App.Author = DS.Model.extend({
name: DS.attr(),
posts: DS.hasMany('post', {async: true}),
email: DS.attr()
});
I map incoming JSON with an AuthorSerializer:
App.AuthorSerializer = DS.RESTSerializer.extend({
normalizePayload: function(type, payload) {
return this.translateRootKey('user', 'author', payload);
},
translateRootKey: function(server_word, client_word, payload) {
var response = {},
key = payload[server_word] ? client_word : Ember.String.pluralize(client_word),
value = payload[server_word] ? payload[server_word] : payload[Ember.String.pluralize(server_word)];
response[key] = value;
return response;
}
});
But I can't figure out how to change the root of my POST/PUT when I'm persisting to the server. I want my root to be "user" or "users" (depending on the situation). The server is currently getting this from Ember as its parameters:
{"author"=>{"name"=>"asdf", "email"=>"asdf"}, "user"=>{}}
How do I tell Ember to use "user/s" as my key name when talking to the server?
Example:
{"user"=>{"name"=>"asdf", "email"=>"asdf"}}
I think you are looking for the serializeIntoHash hook.
App.AuthorSerializer = DS.RESTSerializer.extend({
serializeIntoHash: function(hash, type, record, options) {
hash["user"] = this.serialize(record, options);
}
});
I'm working with forms in Ember.js and I want to retrieve a list of all model properties so that I can take snapshots of the state of the form at different moments. Is there a way to get a list of all properties of a model?
For example, if my model is:
App.User = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
current_password: DS.attr('string'),
password: DS.attr('string'),
password_confirmation: DS.attr('string'),
admin: DS.attr('boolean'),
}
Then I would like to have something like this:
> getEmberProps('User')
["name", "email", "current_password", "password", "password_confirmation", "admin"]
You can simply use toJSON method on model and get the keys from object.
Ember.keys(model.toJSON())
Note that will not return you keys for relations.
You can also use this:
http://emberjs.com/api/data/classes/DS.Model.html#property_attributes
http://emberjs.com/api/data/classes/DS.Model.html#method_eachAttribute
Ember.get(App.User, 'attributes').map(function(name) { return name; });
Ember.get(userInstance.constructor, 'attributes').map(function(name) { return name; });
There's also similar properties for relationships too.
An easy way to print out the fields and their values:
Ember.keys(model.toJSON()).forEach(function(prop) { console.log(prop + " " + model.get(prop)); } )
There is no easy way, but you could try a custom mixin like this:
Ember.AllKeysMixin = Ember.Mixin.create({
getKeys: function() {
var v, ret = [];
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
} // ignore useless items
if (Ember.typeOf(v) === 'function') {
continue;
}
ret.push(key);
}
}
return ret
}
});
You can use it like this:
App.YourObject = Ember.Object.extend(Ember.AllKeysMixin, {
... //your stuff
});
var yourObject = App.YourObject.create({
foo : "fooValue";
});
var keys = yourObject.getKeys(); // should be ["foo"];
in 2018: use Ember Data's eachAttribute.
So, given a model Foo:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
"name": attr('string'),
"status": attr('string')
});
Let's get the model's definition via the constructor:
var record = this.store.createRecord('foo', {
name: "model1",
status: "status1"
});
this.modelClass = record.constructor;
and invoke a function for each of its Ember Data attributes:
modelClass.eachAttribute(function(key, meta){
//meta provides useful information about the attribute type
}
I am rolling a localStorage adaper for Ember data and when I run the find function, ie:
App.store.find(App.Person, 0 );
I am getting this error:
Uncaught Error: assertion failed: A data hash was loaded for a model of type App.Person but no primary key 'undefined' was provided.
At a more general level, I am a bit confused about the relationship between 'the persistent layer' ( in this case localStorage ) and the Ember Store. What does it mean to load something to the store? Does it mean an instance of DS.Person model is created with the data ?
Also, if you'll note I commented out a line 'App.store.didCreateRecord( model, data )' in the createRecord method since it, too is not working. What happens if I do not call store.didCreateRecord after the record has been loaded to localStorage.
The details:
DS.LocalStorageAdapter = DS.Adapter.extend({
get: Ember.get,
set: Ember.set,
createRecord: function( store, modelType, model ){
var records, index, data;
// get existing records of this model
records = this.localStorage.get( modelType );
index = records.length;
data = this.get( model, 'data' );
// set storageID of data
data.set( 'storageID', index );
// add data to existing records
records[index] = data;
// encode records
records = JSON.stringify( records );
// store records in localStorage
this.localStorage.set( modelType, records );
// App.store.didCreateRecord( model, data );
},
find: function( store, modelType, id ) {
var records, model;
records = this.localStorage.get( modelType );
model = records[id];
App.store.load( modelType, model );
},
localStorage: {
set: function( modelType, value ){
localStorage.setItem( modelType, value);
},
get: function( modelType ){
var record = localStorage.getItem(modelType);
record = JSON.parse(record) || [];
return record;
}
}
});
//application model
App.Person = DS.Model.extend({
name: DS.attr('string', {key: 'css_name'}),
storageID: DS.attr('number', {defaultValue: 0, key: 'storageID'}),
});
//create a couple of records
App.store.createRecord(App.Person, { ... } );
App.store.createRecord(App.Person, { ... } );
//try and find one record
App.store.find(App.Person, 0);
error thrown:
Uncaught Error: assertion failed: A data hash was loaded for a model of type App.Person but no primary key 'undefined' was provided. ember-latest.js:51
Ember.assert ember-latest.js:51
(anonymous function) ember-latest.js:131
DS.Store.Ember.Object.extend.load ember-data.js:1530
DS.LocalStorageAdapter.DS.Adapter.extend.find bookApp_1.js:111
DS.Store.Ember.Object.extend.findByClientId ember-data.js:1174
DS.Store.Ember.Object.extend.find ember-data.js:1140
findRecord webApplication.js:210
onclick
also please note this is similar to another question posted a bit ago:
Ember Data: A data hash was loaded ... but no primary key 'undefined' was provided
though the answer there seems to not really explain what is going on.
Can give example of loading Fixture data for Ember 1.0 pre2
App.store = DS.Store.create({
revision: 11,
adapter: 'DS.FixtureAdapter'
});
App.User = DS.Model.extend({
email: DS.attr('string'),
password: DS.attr('string')
});
App.User.FIXTURES = [
{
"id": "1",
"email": "test#mail.com",
"password": "password"
}
];
App.User.find(1).get('email')
Works for me.