PDA

View Full Version : [SOLVED] jsonView works only once in IE :(



DuGi
22 Jun 2007, 2:28 AM
Hey everyone,

I have a really strange problem. My jsonView does only work once. I use jsonView to update my "view window" with the selected e-mail. It shows the content the first time, but then it makes some kind og js-error and then one I wanna view another e-mail IE won't execute the functions because of the former error.

The error I get is:
"Object doesn't support this property or method"

Let me break it down to pieces:

I load the page, where I fetch the 10 newest e-mails and load them into a grid. Then I return a field with a number (1-10) which is the field, that has to be selected from the start. Then I do the grid selection and when I do this, the "show e-mail" function gets executed - precise as it should. This is all good.

But then somewhere in the jsonView it makes the error, which then terminates all other js stuff.

Here are my code:

[code]
/****************************
* Get e-mails
****************************/
// create the Data Store
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: domain+'scripts/mailbox/inbox/list/'
}),

reader: new Ext.data.JsonReader({
root : 'mails',
totalProperty : 'totalCount',
id : 'id'
}, new Ext.data.Record.create([{
name : 'id',
mapping : 'id'
}, {
name : 'status',
mapping : 'status'
}, {
name : 'subject',
mapping : 'subject'
}, {
name : 'from',
mapping : 'from'
}, {
name : 'date',
mapping : 'udate'
}, {
name : 'size',
mapping : 'size'
}
])),

remoteSort : true
});

function mailCheckbox(id) {
return '<p style="text-align: center"><input type="checkbox" name="mails[]" value="'+id+'" style="margin-top: 1px"></p>';
}

var cm = new Ext.grid.ColumnModel([{
header : '<img src="'+domain+'/images/icons/grid_bullet_blue.png" style="width: 16px; height: 13px; cursor: help;" title="Priotet" alt="Priotet"/>',
dataIndex : 'priority',
width : 25,
sortable : false,
resizable : false
},{
header : '<img src="'+domain+'/images/icons/grid_email.png" style="width: 16px; height: 13px; cursor: help;" title="Status" alt="Status"/>',
dataIndex : 'status',
width : 25,
resizable : false
},{
id : 'messsages-head-subject',
header : 'Emne',
dataIndex : 'subject',
resizable : false
},{
header : 'Afsender',
dataIndex : 'from',
width : 250,
resizable : false
},{
header : 'Dato / Klokken',
dataIndex : 'date',
width : 175,
resizable : false
},{
header : 'St

willydee
22 Jun 2007, 3:16 AM
Please have a look into Firebug how many requests are fired on subsequent invocations of loadMail(). I'll bet there is more than one XHR...

I'd strongly recommend not to create the view from scratch anytime loadMail() is called. From my understanding, event handlers sometimes aren't detached from the first instance when creating a new one. Instead, encapsulate the creation of reusable objects—datastores also—into repelling functions (optionally private method) as follows:



// declare a global variable (or better, a private member)
var view = null;
// create an initialization function (or better, a private method)
function getView() {
if (! view) {
// create view and event handlers only once and keep references
view = new Ext.JsonView(...);
view.on('load', function(e) {
readerTop.setTitle(e.jsonData[0].subject);
Ext.get('reader-sender-name').dom.innerHTML = e.jsonData[0].from;
Ext.get('reader-date').dom.innerHTML = e.jsonData[0].udate;
});
}
return this.view;
}

Then your loadMail() function would look similar to this:



loadMail: function(mailid) {
getView().load({
url: domain+'scripts/mailbox/read/',
params: { id : mailid }
});
}

DuGi
22 Jun 2007, 4:44 AM
As told I'm very new to Ext JS, so I'm not sure about what you're saying. But i'll try you code in a bit.

fyi, I only get one XHR pr. "view mail". Everytime I click a new e-mail it makes a new XHR.

Animal
22 Jun 2007, 4:53 AM
Event handlers are never removed from an instance of a class just because you instantiate another instance of the class! Why would they be?

DuGi
22 Jun 2007, 5:37 AM
[quote=willydee;40844]Please have a look into Firebug how many requests are fired on subsequent invocations of loadMail(). I'll bet there is more than one XHR...

I'd strongly recommend not to create the view from scratch anytime loadMail() is called. From my understanding, event handlers sometimes aren't detached from the first instance when creating a new one. Instead, encapsulate the creation of reusable objects

tryanDLS
22 Jun 2007, 9:14 AM
Did you verify in Firebug that you're really not creating duplicate view objects. I didn't try to go thru all your code, but if you have scoping issues and your variable is not what you think it is, you could be recreating the object. Your fn is using a var called view and then returning this.view - are you sure they're are the same?

willydee
22 Jun 2007, 10:58 AM
Event handlers are never removed from an instance of a class just because you instantiate another instance of the class! Why would they be?

Excuse me for popping in. Coming from PHP, my knowledge tells me that an object instance is destroyed as soon as the last reference to it is unset() or overwritten. Is this different in Javascript? Do I have to remove all listeners and delete the object by hand?



var myStore = new Ext.data.Store();
myStore.on('load', ...);
myStore = new Ext.data.Store();


Doesn't this code actually destroy the first instance of the store

DuGi
22 Jun 2007, 1:31 PM
I actually just fixed the IE error. It was in my jsonView constructor.

Before the fix:


mailView = new Ext.JsonView('reader-content', '{body}', {
jsonRoot : 'mail'
});


After the fix:


mailView = new Ext.JsonView('reader-content', '<div>{body}</div>', {
jsonRoot : 'mail'
});


It seems to be, that it was because that my "template" just contained the "{body}" and nothing else. Dont know why. But putting <div>'s around fixed the problem.

Can someone explain this or? :)

Animal
22 Jun 2007, 11:15 PM
Excuse me for popping in. Coming from PHP, my knowledge tells me that an object instance is destroyed as soon as the last reference to it is unset() or overwritten. Is this different in Javascript? Do I have to remove all listeners and delete the object by hand?



var myStore = new Ext.data.Store();
myStore.on('load', ...);
myStore = new Ext.data.Store();


Doesn't this code actually destroy the first instance of the store

I think your thinking is a bit woolly.

Why in dog's name would that destroy (call a method called "destroy" upon it???) whatever object (and it could be ANY object in javascript, or it could be undefined or null) happened to be referred to by "myStore"???????

All that happens is that you lose the ability to refer to that object. It still exists. And listeners are attached to it, it could (if we were talking about a Grid) hold references to DOM objects within the document.



var myStore = new Ext.data.Store();
myStore.on('load', ...);
myStore = 1;


Would you expect THAT to DO something to the object that "myStore" referred to?



var myStore = 1;
myStore.on('load', ...);
myStore = new Ext.data.Store();


What would you want the assignment of "new Ext.data.Store()" to "myStore" do to the literal 1?

I suspect dozens of posters are suffering from this woolly thinking and have objects littered around.

willydee
23 Jun 2007, 12:43 AM
Hello Animal,
"woolly" is a funny word ;-)
As I told, I'm used to automatic garbage collection mechanisms as implemented in PHP and other languages. You're probably right about this thinking being present in a large part of the audience here since many people are migrating from PHP, where this behaviour is clearly documented...

If I create a class instance and assign it to one or more variable names, I expect the instance being __destructed and all of it's memory being freed by the mechanism as soon as the last reference to it is unreferenced (= assigned to something else, or unset()). Nobody told me that this might not be the case in JavaScript, and during my readings through many documentations I never saw an explanation of this behaviour. Considering this knowledge being absolutely required for proper development, it's daunting that I never did stumble over it.

Sorry for being "woolly"; usually it feels snugly, but actually a bit prickly after your reply ;-)

Animal
23 Jun 2007, 4:30 AM
That's the theory, but there's no guarantee that garbage will be collected. References may be all over the place.

Consider adding a listener function. You add a function as a listener and have its scope as the object itself. The closure created holds a reference to the object. A Listener added to a DOM element may have a reference to the object.

You really have to dismantle complex objects if a page is supposed to be long lived. It's not easy. I'm considering forcing a page refresh every so often in my app in the hope that this will rectify memory leaks.

Ext's garbage collection will help. That identifies orphaned Elements which are not in the document, and destroys their attached Ext widgets.

willydee
23 Jun 2007, 4:57 AM
Thanks for explaining, Animal. Apparently one aspect of the closure thing Douglas Crockford and others refer to. Looks like JavaScript is way more different than I was aware of...

A last question: what would be the recommended way to get rid of objects not needed any more? For example, a login/registration form is usually only needed once per session, so it can be dropped after usage. Considering a ContentPanel with a form inside, which is then removed from layout:

myForm.purgeListeners();
delete myForm;
myCenter.remove(myPanel, false);

Would this scheme be sufficient to circumvent common problems?