Extra fields in model - backbone.js

im using Backbone.js to manage models and collections in my app.
So, i have a "Usuario" model that match with a user entity in the database. This is the code:
var Usuario = Backbone.Model.extend({
defaults:{
idusuario: 'undefined',
apellido: 'undefined',
nombre: 'undefined',
dni: 'undefined',
dom_usuario: 'undefined',
nombreusuario: 'undefined',
activo: 'undefined',
bloqueado: 'undefined',
recomendar_cambio_clave: 'undefined',
isChecked: false
}
});
All fields of the model are in the entity user, except "isChecked".The isChecked attribute, will be true when the user model is checcked.
But when i query by "isChecked" js not recognize the variable:
console.log(this.model.get('isChecked'));
undefined
console.log(this.model.get('nombre'));
Horacio José
Any ideas ?.
UPDATE 1
It is a extract of my code, where load the collection (of Usuario) via Ajax:
var anUrl = $("#ajax-call").val();
var myCol = new CustomCollection({
model: Usuario,
filterAttribute: 'apellido'
});
myCol.fetch({async: false, url: anUrl});
myCol.saveOriginal();
console.log(myCol);
When, show in console the collection, the models not contains "isChecked" property:

With the details you've provided it's hard to know if the error is in your view's initialization or anything else. Using this fiddle you can see your example works perfectly. I created a view in that fiddle trying to mimic your scenario.
However, don't confuse 'undefined' or "undefined" with undefined. "undefined" == undefined and "undefined" === undefined are both false.
So, your model should be:
var Usuario = Backbone.Model.extend({
defaults : {
idusuario : undefined,
apellido : undefined,
nombre : undefined,
dni : undefined,
dom_usuario : undefined,
nombreusuario : undefined,
activo : undefined,
bloqueado : undefined,
recomendar_cambio_clave : undefined,
isChecked : false
}
});
And then:
var u = new Usuario();
console.log(u.get("isChecked"));
Should give your expected result.

I solve mi problem. I create dynamically the property "isChecked", querying is the value is defined or not:
toggleModel: function(){
var current = this.model.get('isChecked');
var status = current === undefined ? true : !current;
this.model.set('isChecked',status);
}
Thanks #Meta for the reply.

Related

ExtJS textfield or combobox defaultValue when model has ID = 0

In my Application (ExtJS 4.2.1) I have a window for creating a new record and save it to database.
When I create the window and show it I create a new record and assign it to the window form record:
onNewPunchClick: function(button, e, options) {
var me = this;
var win = Ext.create('App.view.attendance.punch.EditPunch'),
editForm = win.down('#editForm');
var newRec = Ext.create('App.model.attendance.DailyTime', {
AttendanceId: 0,
EmployeeId: 0,
SensorId: 0,
IsManual: true,
IsActive: true
});
// load record to form
editForm.loadRecord(newRec);
win.show();
},
Then my form is displayed but some fields has "0" like this:
So I was trying to workaround but I can't get it working fine:
I have tried using the convert function in the model that returns ' ' when 0 value.
I also have tried to use the defaultValue in the model without any success.
I also have tried to set EmployeeId: '', when creating the model with no success.
There should something that I'm missing, any help is really appreciated.
The solution that works great is:
In your model set the property that you want to have the desired behavior like this:
{ name: 'EmployeeId', type: 'int', defaultValue: '', convert: null },
Then when you create the model set the property to undefined.
var newRecord = Ext.create('CustomModel',{
EmployeeId: undefined
});
Then load the form:
form.loadRecord(newRecord);
And after you submit the form:
form.updateRecord(newRecord);

backbone.js a url property must be defined

I'm trying to use Backbone.localStorage as a backend to an app.
I wrote a Model called Era, and a corresponding EraView. Pressing enter in the EraView causes the model to be saved, and this is where I get the error:
Uncaught Error: A "url" property or function must be specified
urlError backbone.js:1509
_.extend.url backbone.js:515
_.result underscore-min.js:1060
Backbone.sync backbone.js:1410
Backbone.sync backbone.localStorage.js:188
_.extend.sync backbone.js:276
_.extend.save backbone.js:476
karass.EraView.Backbone.View.extend.close era.js:61
karass.EraView.Backbone.View.extend.updateOnEnter era.js:75
Here is the code to the EraView
var karass = karass || {};
// Era Item View
// the DOM element for an era item
karass.EraView = Backbone.View.extend({
tagName: 'li',
className: 'era',
template: _.template( $('#era-template').html() ),
// The DOM events specified to an item
events: {
'dblclick .edit-input': 'edit',
'keypress .edit-input': 'updateOnEnter',
//'blur .edit': 'close',
},
// The EraView listens for changes to its model, re-rendering. Since there's
// a one-to-one correspondence between an era and a EraView in this karass,
// we set a direct reference on the model for convenience.
initialize: function(){
_.bindAll(this);
this.model.on('change', this.render, this);
},
// Re-renders the era item to the current state of the model and
// updates the reference to the era's edit input within the view
render: function(){
this.$el.html( this.template(this.model.attributes));
this.$era_start = this.$('.era-start');
this.$era_end = this.$('.era-end');
this.$era_start.attr('disabled', true);
this.$era_end.attr('disabled', true);
return this;
},
// Switch this view into editing mode, displaying the input field
edit: function(){
this.$('.edit-input').removeAttr('disabled');
this.$el.addClass('editing');
this.$('.edit-input').addClass('editing');
},
// Close the editing mode, saving changes to the era
close: function(){
this.$('.edit-input').attr('disabled', true);
var start = this.$era_start.val().trim();
var end = this.$era_end.val().trim();
if(start && end){
this.model.save({from: start, until: end});
}
this.$el.removeClass('editing');
this.$('.edit-input').removeClass('editing');
this.trigger('close');
},
updateOnEnter: function(e){
if(e.which !== ENTER_KEY && (!this.$era_start.val().trim() || !this.$era_end.val().trim())){
return;
}
this.close();
}
});
And this is the code for the era model:
var karass = karass || {};
karass.Era = Backbone.Model.extend({
defaults: {
from: '',
until: ''
},
});
I thought I didn't need a url while using localStorage.
Edit: I forgot to mention that while this behavior occurs in the Era/EraView itself, it also occurs in the Name model, which extends Era. Name in turn belongs in a Names collection. I don't know if this makes a difference, but I figured I add it.
Edit 2: The Names collection looks like this:
karass.Names = History.extend({
model: karass.Name,
localStorage: new Backbone.LocalStorage('karass-names'),
});
Edit 3: I posted all the code on jsfiddle: http://jsfiddle.net/herrturtur/CRv6h/
You don't need an url while using localStorage. But you need to set the localStorage property on your model or on your collection (if you set the localStorage on a collection the models inside the collection will "inherit" this setting):
karass.Era = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("EraCollection"),
// the EraCollection should be an unique name within your app.
defaults: {
from: '',
until: ''
},
});
If you don't setup the localStorage property the plugin falls back to the default ajax sync so you get the uri missing exception.

Search collection and retrieve model backbonejs

I am trying to search a collection for a model attribute and then grab and return the entire model ?
var myModel = Backbone.Model.extend({
defaults: {
a: '',
b: '',
c: '',
d: '',
e: ''
}
});
My collection has around 100 of myModels.
I am trying to search through the collection by a, find it and then return the entire myModel of a so I can access the other attributes ?
If I understand your question correctly, you want to use the where method on Backbone collections, here in the docs:
http://backbonejs.org/#Collection-where
So, given an instance of MyCollection called myCollection that has MyModels in it, you can say:
var foundModels = myCollection.where({a:'some value'});
and foundModels will contain an array of the models you seek
BTW, if you are doing a more complex search, use the filter method instead, passing a function as the first argument that returns true on the desired match:
var modelsWhoseAStartsWithA = myCollection.filter(function(anyModel) {
var startsWithA = new RegExp(/^[aA]/);
return startsWithA.test(anyModel.get('a'));
});

How are 'change' events handled in backbone models?

I'm trying to build a model that dynamically updates Session variables in a Meteor project. I know that plain JSON should not be stored within backbone models, so I have a Special model set up like so:
initialize : function () {
// Log the changed properties
this.on('change', function (model, options) {
for ( var i in options.changes)
this.display(i);
Session.set('NewSpecial', model);
});
},
//Attributes
defaults: {
"Product" : null,
"ShortDescription" : null,
"Category" : "food",
"Price" : new PriceModel,
"Date" : new DateModel,
"Uses" : 0,
"Tags" : [],
"Contributor" : null
},
With "Price" and "Date" being stored in their own models:
//Price model for use within Special
var PriceModel = Backbone.Model.extend({
defaults : {
"Regular" : null,
"Special" : null,
"PercentOff" : null
}
});
//Date model for use within Special
var DateModel = Backbone.Model.extend({
defaults : {
"StartTime" : null,
"EndTime" : null,
"HumanTimeRange" : null
}
});
As shown, when the attributes of the Special model change, it should call display for the attribute that changed, and then set the Session var to the model. If my DateModel or PriceModel change however, it doesn't appear to trigger a change event on the Special model. Should each "DateModel" and "PriceModel" have their own this.on('change', ...) methods that call Special.set(attribute, thisModel) methods? Or is there a different way to go about this?
I see a couple problems.
First of all, your defaults:
defaults: {
"Product" : null,
"ShortDescription" : null,
"Category" : "food",
"Price" : new PriceModel,
"Date" : new DateModel,
"Uses" : 0,
"Tags" : [],
"Contributor" : null
}
That will end up with one PriceModel, one DateModel, and one tags array being shared by all instances of that model. A defaults object is shallow copied and merged into the model's attributes, none of the values in defaults are cloned or duplicated, they're just copied over as-is. If you want distinced Price, Date, and Tags values then use a function for defaults:
defaults: function() {
return {
"Product" : null,
"ShortDescription" : null,
"Category" : "food",
"Price" : new PriceModel,
"Date" : new DateModel,
"Uses" : 0,
"Tags" : [],
"Contributor" : null
};
}
The second problem is that set has a fairly simplistic view of what change means. If you have a look at the source for set, you'll see this:
// If the new and previous value differ, record the change. If not,
// then remove changes for this attribute.
if (!_.isEqual(prev[attr], val) || (_.has(now, attr) !== _.has(prev, attr))) {
this.changed[attr] = val;
if (!silent) this._pending[attr] = true;
} else {
delete this.changed[attr];
delete this._pending[attr];
if (!changing) delete this._changes[attr];
}
The _.isEqual won't recognize that something has changed inside your Price or Date or that you've added or removed something from Tags. If you do things like this:
p = new PriceModel(...);
m.set('Price', p)
then m will noticed that Price has changed but if you:
p = m.get('Price');
p.set(...);
m.set('Price', p);
then m won't recognize that Price has changed; your model won't automatically bind to events on Price so it won't notice the p.set(...) call and it won't recognize m.set('Price', p) as a change since that's little more than a fancy way of saying p = p.
You can solve part of this change problem by not giving set a Tags array that came from get; make a copy, change the copy, and then hand the updated copy to set. The half can be handled by binding to "change" events on the contained Price and Date models and forwarding them similar to how collections do it, something like this:
initialize: function() {
this.attributes.Price.on(
'all',
function(ev, model, opts) { this.trigger(ev, model, opts) },
this
);
//...
}
You'd want to provide your own set implementation in case someone did a set('Price', some_new_object) and you need to rebind your forwarder.

Backbone.js - Using new() in Model defaults - circular reference

Taking the following Model:
MyModel= Backbone.Model.extend({
defaults : {
myNestedModel:undefined,
},
initialize: function() {
this.set({myNestedModel: new MyNestedModel());
}
});
It has a single property named 'myNestedModel' which has the following definition:
MyNestedModel= Backbone.Model.extend({
defaults : {
myModel:undefined,
}
});
It too has a single Property name 'myModel'. Now if I create an instance of MyModel:
aModel = new MyModel();
The nested model will have been set in MyModel's initialize method. I then use JSON.stringify in a two step process:
// Use Backbone.js framework to get an object that we can use JSON.stringfy on
var modelAsJson = aModel.toJSON();
// Now actually do stringify
var modelAsJsonString = JSON.stringify(modelAsJson);
This works fine and I get the JSON representation of MyModel and it's property of MyNestedModel. The problem occurs when I use defaults, for example:
MyModel= Backbone.Model.extend({
defaults : {
new MyNestedModel(),
}
});
This causes a problem with JSON.stringify since it doesn't support circular references. I assume the circular reference is being created because all instances of MyModel share the same instance of MyNestedModel. Whereas the initialize method creates a new nested model for each instance.
Questions:
Is my understanding of defaults:{} being the 'cause' of the
problem correct?
From a question I posted recently I got the
impression I should be using defaults for all properties. If that is
the case, how should I be using defaults in the scenario presented
in this post/question?
Can someone clarify the use of defaults:{}
with regards to when the value applies, when it's overridden and
whether instances share the same default 'instances'?
Defaults is used only for attributes inside your model ( the data in the model ), and whenever you create your model it takes the values from defaults and sets the attributes. e.g.
User = Backbone.Model.extend({
defaults : {
rating : 0
}
})
User1 = new User({ name : 'jack', email : 'jack#gmail.com' });
User2 = new User({ name : 'john', email : 'john#gmail.com' });
User1.set({ rating : 2 });
Now your two models when called with toJSON will print
{
rating: 2,
name: 'jack',
email: 'jack#gmail.com'
}
{
rating: 0,
name: 'john',
email: 'john#gmail.com'
}
Since defaults is an object, every value you place there is evaluated immediately so :
defaults : {
rating : defaultRating()
}
will call defaultRating() - not everytime when you initialize the model, but immediately ( in the extend method )
You should use defaults for models where you need some data to exist on the creating of the model ( e.g. new myModel() )
In your example you have the following mistakes :
1.set a value without a property
defaults : {
PROPERTY : new Model()
}
2.you don't need such an option for your defaults - you should place there only attributes ( data ) for the model
Defaults applies always as long as it is not replaced by a new defaults in an extended model e.g.
var Model = Backbone.Model.extend({ defaults : { alpha : 'beta' } });
var myModel = Model.extend({ defaults : { beta : 'gama' } });
now your myModel when initialized will have
{ beta : 'gama' } // alpha : 'beta' will not be set as value, because it is replaced

Resources