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 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've got a number of backbone models which have a number of nested sub-models. My solution looks like this:
Models.Base = Backbone.Model.extend ({
relatedModels: {},
/**
* Parses data sent according to the list of related models.
*
* #since Version 1
* #param {Object} response Response
* #return {Object} Parsed data
*/
parse: function (response) {
var key,
embeddedClass,
embeddedData;
for (key in this.relatedModels) {
embeddedClass = this.relatedModels[key];
embeddedData = response[key];
response[key] = new embeddedClass (embeddedData, { parse: true });
}
return response;
}
});
(using stuff gleaned from this post - Nested Models in Backbone.js, how to approach)
This works fine whilst I'm getting stuff from the server:
Models.Individual = Models.Base.extend({
idAttribute: "idInd",
urlRoot: "data/individuals/save",
relatedModels: {
details: Collections.DetailList,
relationships: Collections.RelationshipList
}
});
... but when I try and initialise a model from a plain bit of JSON, for example if I were to do this:
var Ind = new Models.Individual ({
idInd: 1,
name: "Bob Holness",
details: [
{ option: "I'd like an 'e' please, bob" },
{ option: "Can I have a 'p' please, bob" }
],
relationships: []
});
... it doesn't seem to want to parse "details". I'd guess that was because it's not running the Parse function, but anyway - how can I get it to parse the data in both instances?
The easiest way to do it would be to pass parse: true to the constructor, like so:
var Ind = new Models.Individual ({
idInd: 1,
...
}, { parse: true });
If you do this a lot you can override the constructor in your base class and make it pass parse: true every time you create a new model instance:
Models.Base = Backbone.Model.extend({
constructor: function(attributes, options) {
var opts = $.extend({}, options || {});
if (_.isUndefined(opts.parse)) {
opts.parse = true;
}
Backbone.Model.call(this, attributes, opts);
},
...
});
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'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 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;
}
});