Getting default values from Backbone.Model in a method? - backbone.js

I am trying to get the default values to use under a method. It seems like Model.get() cannot be used within the method itself. I have tried the two console.log, which displays the variable is not defined.
define(['jquery', 'Underscore', 'Backbone'], function($, _, Backbone){
var Game = Backbone.Model.extend({
//default values for the function
defaults: {
rows : 9,
cols : 8,
baseScore : 100,
numBlockTypes : 6,
baseLevelTimer : 60000,
baseLevelScore : 1500,
baseLevelExp : 1.05,
},
initialize: function(){
console.log(this.get(numBlockTypes));
//console.log(numBlockTypes);
}
}),
game = new Game;
return game;
});

According to your question (get default values), the answer should be
this.defaults.numBlockTypes
Mentioned method
this.get("numBlockTypes")
returns current (set) values.

Both #Elvis D'Souza and I came up with the same answer at the same time.
Need quotes
console.log(this.get('numBlockTypes'));

Related

how to pass values from one view to another view extjs?

I am trying to pass values from one view to another view, Below is the code what I have tried so far but no luck,
view1.js
var newWindow = Ext.create( 'newView', {"clickedRule" : clickedvalue} );
signerWindow.show();
newView.js
Ext.define('newView', {
extend : 'Ext.window.Window',
id : 'newWindow',
constructor : function (clickedRule){
Ext.applyIf(this, clickedRule);
this.callParent();
},
requires : ['Ext.form.Panel'],
title : 'Title Selection'+ this.clickedRule
});
I am getting undefined, your help is greatly appreciated.
Thanks in advance.
Do it in Ext.create:
var newWindow = Ext.create( 'newView', {
clickedRule : clickedvalue,
title : 'Title Selection ' + clickedValue
});
newWindow.show();
and lose the title config on the class:
Ext.define('newView', {
extend : 'Ext.window.Window',
id : 'newWindow',
constructor : function (clickedRule){
Ext.applyIf(this, clickedRule);
this.callParent();
},
requires : ['Ext.form.Panel']
});
Edit from comment:
You will have to put it in the initComponent function in that case:
this refers to an instance of your window class so won't be available in the class definition (config statements) but you can access it after the class has been initialized using the initComponent function:
Ext.define('newView', {
extend : 'Ext.window.Window',
id : 'newWindow',
initComponent: function (){
var clickedRule = this.clickedRule;
// title can also be defined here
this.title = 'Title Selection ' + clickedRule;
this.callParent();
},
requires : ['Ext.form.Panel']
});
initComponent is the normal way of adding constructor logic to ExtJS components. See this in the docs.
Keep your original create statement:
var newWindow = Ext.create( 'newView', {clickedRule : clickedvalue} );
You can access the clickedRule property after the create statement:
console.log(newWindow.clickedRule);

Why use Ext.apply in initComponent

A lot of code examples use Ext.apply when setting properties on a component in the initComponent method.
Example :
initComponent: function(){
Ext.apply(this, {
items: {
xtype: 'button'
}
})},
My question is, what is the difference in doing it that way, compared to doing it this way :
initComponent: function(){
this.items = {
xtype: 'button'
}
}
For me it seems more readable that way. But am I missing something that I get from Ext.apply?
Ext.apply() is used to simplify the copying of many properties from a source to a target object (most of the time the source and target objects have different sets of properties) and it can in addition be used to apply default values (third argument).
Note that it will not make deep clones! Meaning if you have a array or a object as property value it will apply the reference!
There is also a applyIf() which only copies properties that do not already exist in the target object. In some cases both implementations have also the benefit of dropping the reference of the copied object.
Note:
Your second way won't work because you are missing this.
initComponent: function(){
items = {
xtype: 'button'
}
}
wouldn't initialize anything, you mean
initComponent: function(){
this.items = {
xtype: 'button'
}
}
which does the same like your example using Ext.apply. But Ext.apply shows its power in more complex cases, e.g.
var x = {a: 1, c:3, e:5};
Ext.apply(x, {b:2, d:4, f:6});
console.log(x); // Object {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}
This is often used to overwrite default options of components with given init parameters.

Ext.define confusion

I'm having an issue which makes me think I might be misinterpreting the use of the ExtJS class architecture a bit. Herewith an example of a simplified version which demonstrates the issue
Ext.define('Person', {
name : 'Default',
inventory : [],
addInventoryItem : function(item) {
Ext.Array.push(this.inventory, item);
},
inventoryList : function() {
console.log("Inventory: " + JSON.stringify(this.inventory));
},
setName : function(name) {
this.name = "name";
}
});
Ext.define('Student', {
extend : 'Person'
});
alex = Ext.create('Student');
alex.setName("Alex");
alex.addInventoryItem("Knifes");
alex.inventoryList();
david = Ext.create('Student');
david.setName("David");
david.addInventoryItem("Forks");
david.inventoryList();
Output:
Inventory: ["Knifes"]
Inventory: ["Knifes","Forks"]
Expected Output:
Inventory: ["Knifes"]
Inventory: ["Forks"]
I would've expected the actual result in the event of overriding the class, but fail to understand why my second instance affects the superclass, and in turn reflects changes made by my first instance.
The problem is that there is a single inventory array shared between all instances of your class. The array is created once at the time the class is defined, then each instance just gets a reference to that one array.
Basically you need to give each Person instance its own inventory array on creation, like this:
Ext.define('Person', {
...
constructor: function() {
this.inventory = [];
},
...
});

Backbone-relational fetchRelated not sending request

I'm using backbone.js and backbone relational 0.5.0 with a Rails 3.2 backend. I have a Card model which has_many Notes.
Here are my JS models and collections:
Workflow.Collections.Cards = Backbone.Collection.extend({
model: Workflow.Models.Card,
url: '/cards'
});
Workflow.Models.Card = Backbone.RelationalModel.extend({
modelName : 'card',
urlRoot : '/cards',
relations: [
{
type: Backbone.HasMany,
key: 'notes',
relatedModel: 'Workflow.Models.Note',
collectionType: 'Workflow.Collections.Notes',
includeInJSON: false,
reverseRelation: {
key: 'card',
includeInJSON: 'id'
}
}]
});
Workflow.Collections.Notes = Backbone.Collection.extend({
model: Workflow.Models.Note,
url: '/cards/74/notes' // intentionally hard-coded for now
});
Workflow.Models.Note = Backbone.RelationalModel.extend({
modelName : 'note',
urlRoot : '/notes'
});
Normal fetching works great, but when I try fetchRelated in the console, I get an empty array:
card = new Workflow.Models.Card({id: 74}) // cool
card.fetch() // hits the sever with GET "/cards/74" - works great
card.fetchRelated('notes') // [] - didn't even try to hit the server
What's weird is that this works:
card.get('notes').fetch() // cool - GET "/cards/74/notes"
I could use that method and parse the response text, but it feels really dirty.
Anyone know what I'm missing here?
Thanks in advance, this one is really torturing me!
Stu
You should create Card with Note ids array: card = new Workflow.Models.Card({id: 74, notes: [74, 75]}); and change the url method of Notes accordingly:
Workflow.Collections.Notes = Backbone.Collection.extend({
model: Workflow.Models.Note
});
Workflow.Models.Note = Backbone.RelationalModel.extend({
modelName : 'note',
urlRoot : function () {
return this.get('card').url() + '/notes';
}
});
card = new Workflow.Models.Card({id: 74, notes: [74, 75]});
card.fetchRelated('notes');
http://jsfiddle.net/theotheo/5DAzx/
I should have posted my solution a while back - there might well be a better way, but this is the convention I've gone with:
All of the following code is in the card view (which is where the notes are displayed).
First, I bind a renderNotes method to the 'reset' event on the card's notes collection:
initialize: function () {
_.bindAll(this);
this.model.get('notes').on('reset', this.renderNotes);
var self = this;
this.model.get('notes').on('add', function(addedNote, relatedCollection) {
self.renderNote(addedNote);
});
}
I also bind to the 'add' on that collection to call a singular renderNote.
The renderNotes and renderNote methods work like this:
renderNotes: function () {
if (this.model.get('notes')) {
this.model.get('notes').each(this.renderNote);
}
},
renderNote: function (note) {
var noteView = new Workflow.Views.Note({ model: note });
this.$('.notes').append(noteView.render().el);
},
Then, the last piece of the puzzle is to actually hit the server up for the card's notes (which will in turn fire the 'reset' event I bound to above). I do this in the card view's render method:
render: function () {
// render all of the eager-loaded things
this.model.get('notes').fetch();
return this;
},
As #user1248256 kindly helped me work out in the comments on my OP, the confusion was mainly in that I expected fetchRelated to pull down lazy-loaded records - that's actually not the case.
As a side-note, this view is actually a modal and be opened and closed (removed from the page). To prevent the zombie events problem described in this excellent post, I also manually unbind the events mentioned above.

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