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
}
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
}
I have a backbone model and collection. In the model, the default attribute values are defined:
var Person = Backbone.Model.extend({
defaults: {
name: "mark",
middle: "-"
}
});
var People = Backbone.Collection.extend({
model: Person
});
var collection = new People();
collection.add({name: "paul", middle: null});
console.log('collection is ');
console.log(collection);
I want the default value for "middle", which is "-", to be taken if "null" is passed in for the attribute "middle". However, "null" overrides the default instead. How do I do this? The jsfiddle is here
The cleanest way probably is to add a parse method and normalize the data:
var Person = Backbone.Model.extend({
defaults: {
name: 'mark',
middle: '-'
},
parse: function (payload) {
return {
name: payload.name || undefined,
middle: payload.middle || undefined
};
}
});
collection.add({name: "paul", middle: null}, {parse: true});
Note that when the data is returning from the server via fetch it will automatically go through parse and there's no need to call pass the option flag.
You could do it either on the model level or at the collection level.
check for value when initialize
initialize: function() {
if (!this.get("middle") || $.trim(this.get("middle") || '') === '') {
this.set({"middle": this.defaults.middle});
}
},
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();
}
}
});
This is for parsing and binding a json object as an attribute (there's a transform as well, but it's very basic):
cleanCredentials: ->
creds = #get('credentials')
Object.keys(creds).forEach (key) =>
unless key in #get('selectedDriver').api_keys
delete #get('credentials')["#{key}"]
get_set: ->
key = arguments[0][0]
value = arguments[0][1]
if (arguments[0].length > 1)
#set "credentials.#{key}", value
#cleanCredentials()
#get "credentials.#{key}"
getter_setter = `function(key, value) { return this.get_set(arguments); }.property('credentials')`
apiToken: getter_setter
applicationId: getter_setter
applicationUserId: getter_setter
companyCode: getter_setter
username: getter_setter
It works, and I understand it, but is there a better way?
IIUC, you should define custom transform:
App.RawTransform = DS.Transform.extend({
deserialize: function(json) {
return json;
},
serialize: function(object) {
return object;
}
});
App.Model = DS.Model.extend({
rawJSONobject: DS.attr('raw')
});
This solution won't be able to bind to individual items within the json string but will watch the entire json string... Also, haven't been able to test this, but hopefully this will give you some ideas.
App.Person = Ember.Model.extend({
// json string
'jsonData' : DS.attr('string'),
// computed property
'parsedData' : function(key, value) {
// setter -- take an object and save the stringified version to the model
if (arguments.length > 1) {
this.set('jsonData', JSON.stringify(value));
}
// return the parsed json string rather than the string itself...
return $.parseJSON(this.get('jsonData'));
}.property('jsonData')
});
I try to use backbone in my project. But I have met problem when try to overide parse method of Backbone. The server has send back more data than I want.For example:
What I want is:
[{
id: "123",
name: "david"
},{
id: "456",
name: "kevin"
}]
but the server's result is :
{
total: 1000,
items:[{
id: "123",
name: "david"
},{
id: "456",
name: "kevin"
}]
}
so I want process result in parse method and return only the array. How can I do this? When I try I got error. How should I do?
In your backbone model, define the parse function the way you would like:
Model = Backbone.Model.extend({
parse: function () {
return {
id: this.get("id"),
name: this.get("name")
}
}
});
But, it would be better to handle and set the data in the model initializer, like so:
Model = Backbone.Model.extend({
initialize: function (attrs) {
try {
//TODO HERE: test for correct values and throw errors
// set object variables here
this.set({
name: attrs.name,
id: attrs.id
});
} catch (e) {
console.log(e);
}
}
});
No need to overwrite the parse function now. This way you know the data that your model is handling is good, and you set what variables it contains. This avoids many errors from invalid data.
Each item in the array should really be a submodel, which is what I have written above. Your parent model should look like:
Model = Backbone.Model.extend({
initialize: function (items) {
this.subModels = [];
items.forEach(function (item) {
this.subModels.push( new SubModel(item) )
});
}
});
Or as a collection:
Collection = Backbone.Collection.extend({
model: ItemModel,
});
To which you would pass response.items
From Backbone Parse docs
Collection = Backbone.Collection.extend({
model: YourModel,
parse: function(response){
return response.items;
}
});