PDA

View Full Version : Class Pattern Problem



Dumas
6 Mar 2010, 2:17 PM
Hi!

What would be the right pattern for this? Following code doesn't work as expected:


var Class = Ext.extend(Ext.Component, {
icons: [],
initComponent: function() {
this.icons[this.icons.length] = 'icon for '+this.name;
}
});

var A = new Class({name:'A'});
var B = new Class({name:'B'});


console.info(B.icons);
expected result:
["icon for B"]

real result:
["icon for A", "icon for B"]

thx
Dumas






PS: Here are some testcases:

// test function
function testForEqual(excepted, result) {
console.info(((Ext.encode(result)===excepted) ? 'success' : 'fail') + ': ' + Ext.encode(result) + ' should be ' + excepted);
}

// tests
var a = new Class({name:'A'});
var b = new Class({name:'B'});

testForEqual('["icon for A"]',b.icons);
testForEqual('[]',Class.prototype.icons);

var c = new Class({name:'C',icons:["C start icon"]});
testForEqual('["C start icon","icon for C"]',c.icons);


var ClassB = Ext.extend(Class, {icons: ["standard icon"]});
var d = new ClassD({name:'D'});
var e = new ClassD({name:'E'});
testForEqual('["standard icon","icon for E"]',e.icons);
testForEqual('["standard icon"]',ClassB.prototype.icons);

Animal
6 Mar 2010, 2:36 PM
http://i131.photobucket.com/albums/p286/TimeTrialAnimal/extendparams.jpg

ZeusTheTrueGod
6 Mar 2010, 10:01 PM
Hi, Dumas
You have 2 main errors in your code. Animal pointed that call of initComponent is required.
no init component - no result at all and random error from ExtJs.


var Class = Ext.extend(Ext.Component, {
icons: [],
initComponent: function() {
this.icons[this.icons.length] = 'icon for '+this.name;
Class.superclass.initComponent.apply(this,arguments); // <-------- initComponent parent call required
}
});

But Animal didn't tell you about you real problem: you made more hard to find error.
look at your icons: []. Icons has a type of array. I can't tell you about javascript theory - you should read it yourself, but here is a simple rule on Ext.extend
width: 500 -> ok , simple number ,byval
height:'auto' -> ok, simple string, byval
array:[1,2,3] -> sucks, pointer to array, byref
errors:{I:'am',learning:'Javascript',to:'understand',ext:'js'} -> sucks, pointer to an object
store: new Ext.data.JsonStore){}) -> sucks, pointer to an object, byref

array, errors and store in my example are SHARED PER CLASS. learn theory about prototypes in javascript and you will understand why the are shared.

and, of course, here is a simple solution when your icons should not be shared

version a) - initialize in initComponent using this.icons = [];



var Class = Ext.extend(Ext.Component, {
//icons: [], <------------ not here!
initComponent: function() {
this.icons = ['icon for ' + this.name]; //instance level variable, not a class level

this.icons[this.icons.length] = 'icon for '+this.name;
Class.superclass.initComponent.apply(this,arguments); // <-------- initComponent parent call required
}
});


version b) - declare a function which returns a default set of something . noe that it fits better to constant declaration



var Class = Ext.extend(Ext.Component, {
getColumnModel: function(){ return new Ext.grid.ColumnModel({dataIndex:'name'}) },
getIcons: function(){
this.icons = this.icons || ['a','b','c','d'];
return this.icons
}
initComponent: function() {
Class.superclass.initComponent.apply(this,arguments); // <-------- initComponent parent call required
}
});


Hope that will help you and may be this theme should be sticky or it is already included in tutorial, because it is a key to understanding how Ext.extend works

Animal
6 Mar 2010, 10:20 PM
It's simpler than all that. The key is just reading "and therefore shared between all instances..."

Maybe I'm old fashioned.

Maybe I should give up endeavouring to create balanced, even, descriptive sentences in documentation, and expecting people to read and internalize it.

When I was at school, we had "comprehension" as part of English lessons. We would have to read a passage, and then answer questions about it.

And we had to answer in full, descriptive sentences, not one word answers. You had to have parsed and digested the passage into internal meaning.

Did this form of teaching fall out of fashion?

ZeusTheTrueGod
6 Mar 2010, 10:57 PM
Animal, I agree with you.
But we have a computer, computer can help us with finding errors. Some people want to learn ExtJs fast, so ExtJs should help them even if they don't know what is prototype and what is 'shared between instances'. Also may be their mind is somehow limited so they can not read the documentation.

Why not to make an Ext.override with a check that only primitives and functions are passed as arguments? If someone realy want a shared array or object - he can do that explicitly with
Ext.apply(myClass.prototype, { }). Word prototype here means that programmer at least know something about prototype.

Ext.extend(Ext.component, { ... }) doesn't containts a word prototype or shared. You can expect from yourself that you read documentation and understand ExtJs and javascript internals. You can expect that from Ext developer team, may be from its support team. Others need some help - from ExtJs error message or may be it will be good to write a simple ExtJs jslint

Dumas
8 Mar 2010, 11:25 AM
@ Animal: I have the superclass.initComponent call in my real class, sry forgot to add it here....
You are right, I didn't made a full sentenses about my expectations and my real result, but I thought with the code and the test cases I'm quite clear about the problem.


@ZeusTheTrueGod: I understand the js theory and why the problem occurs, I just falsly thought that the objects are copied for the new object, which of course not really make sense. It's just that this pseudo-classical way of classes let's me somethings forget the prototype nature of js. Thanks for reminding me ;-)


My goal was to have a configurable icon class, so I guess I will have to make something like this?! Any better solutions?


var Class = Ext.extend(Ext.Component, {
icons: ['standard icon'],
initComponent: function() {
this.icons = makeDeepCopy(this.icons);
this.icons[this.icons.length] = 'icon for '+this.name;
Class.superclass.initComponent.apply(this,arguments);
}
});

thx
Roland

Animal
8 Mar 2010, 12:45 PM
The "icons" property will be shared.

If you want a default set of icons shared, you must create a defaultIcons member in the prototype.

You need to do a deep copy, but Array has a method for that so



MyClass = Ext.extend(Ext.Component, {

defaultIcons: ['standard icon'],

initComponent: function() {
this.icons = this.defaultIcons.slice();
this.icons.push(this.configuredIcon);
console.log(this.icons);
}
});
new MyClass({ configuredIcon: 'icon 1'});
new MyClass({ configuredIcon: 'icon 2'});

Dumas
12 Mar 2010, 1:47 PM
My configurations object is in reality not just an array, uses e.g. Sakis rowActions which alters the coumn model, so I have to use a deep copy function.

Thx for your help!
Roland