Use Backbone model default if a attribute value is null - javascript

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});
}
},

Related

Get all attributes of Ember.Object [duplicate]

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
}

How can I fake Collection data?

What I mean by this is I want to create it artificially.
This is for testing purposes.
But for models, it is quite simple. I just set defaults I instantiate the model object and from there I can use this.model.toJSON() to grab the created data.
I want to use this same trick with collections. Is there a similar way to do this with collections? What I would want to do is have the collection create x ( 8 in this case ) copies of Model defaults.
Basically what I was doing before for models but a little bit more complex as it applies to Collections.
Here is the actual use case. It should be simple.
/**Model
**/
// name, picture, time, tweet, h_file
var FeedRow = Backbone.Model.extend({
Name: 'FeedRow',
defaults: {
name: "default",
picture: 0,
time: "0",
tweet: "default",
h_file: "default"
}
});
/**Collection
**/
var FeedTable = Backbone.Collection.extend({
Name: 'FeedTable',
model: FeedRow
});
When your FeedTable collection is constructed you could set the model on it multiple times in the initialize method.
var FeedTable = Backbone.Collection.extend(
{
Name: 'FeedTable',
model: FeedRow,
initialize: function()
{
model = this.model;
models = [];
_.times(8, function(n)
{
models.push(new model({id: (n + 1)}));
});
this.set(models);
}
});

How to retrieve all properties of an Ember.js model

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
}

How to get model attributes within a view in Backbone.js?

I am trying to pass a model as parameter in a view.
I am getting my object in the view, but no way to access its attributes...
Here is the code :
From the router :
var Product = new ProductModel({id: id});
Product.fetch();
var product_view = new ProductView({el: $("#main-content"), model: Product});
From the model :
var ProductModel = Backbone.Model.extend({
urlRoot: 'http://192.168.1.200:8080/store/rest/product/'
});
From the view :
ProductView = Backbone.View.extend({
initialize: function(){
console.log(this.model);
this.render();
},
render: function(){
var options = {
id: this.model.id,
name: this.model.get('name'),
publication_start_date: this.model.get('publication_start_date'),
publication_end_date: this.model.get('publication_end_date'),
description: this.model.get('description'),
applis_more: this.model.get('applis_more')
}
var element = this.$el;
var that = this;
$.get('templates/product.html', function(data){
var template = _.template(data, options);
element.html(template);
});
}
});
Here is the result of the "console.log":
child {attributes: Object, _escapedAttributes: Object, cid: "c1", changed: Object, _silent: Object…}
Competences: child
Editor: child
Hobbies: child
Opinions: child
_changing: false
_escapedAttributes: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object
createdDate: null
deletedDate: null
descriptionId: 0
galleryImage: null
id: 13
image: null
manufacturerId: 0
name: "sdf"
owner: 0
status: 0
thumbnail: null
titleId: 0
type: 1
uid: "fdsf"
updatedDate: null
__proto__: Object
changed: Object
cid: "c1"
id: 13
__proto__: ctor
In my view, all my options are "undefined" (name, dates,...)
Any idea on what I am doing wrong ?
After creating the initial model, you're immediately creating the view using
var product_view = new ProductView({..., model: Product});
In the initialize method of ProductView, you're calling this.render(), which in turn reads and renders values from the model - most of these values are undefined (because the server did not have sufficient time to send back the model's values).
Don't call this.render() directly, but bind an event, eg:
// in ProductView::initialize
this.model.on('sync', function() {
this.render();
}, this);
You might also want to bind the change event, so that local (not synchronizsed yet) changes to the model are also reflected in the view. (An overview of Backbone events can be found here.)
Because Product.fetch() happens asynchronously, you'll want to create the view once the data has been retrieved from the server:
var Product = new ProductModel({id: id});
var product_view;
Product.fetch().done(function() {
product_view = new ProductView({el: $("#main-content"), model: Product});
});
Product.fetch() is an asynchronous call, so my guess is that the fetch has not completed yet when you initialize the view. What you probably want to do is use fetch's success + error callbacks, to assure the model has data in it before rendering anything
http://backbonejs.org/#Model-fetch

how to override Backbone's parse function?

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;
}
});

Categories

Resources