PDA

View Full Version : Uncaught RangeError when using Ext.encode



simonr25
3 Jan 2012, 12:54 PM
Hello,
version of ext js 4 I am using: 4.0.7.
I am getting the stack trace below when using Ext.encode
with record returned from getRecord or getFieldValues.


Uncaught RangeError: Maximum call stack size exceeded
encodeString
doEncode
encodeObject
doEncode
...


The code reads data into a grid and when user selects a row,
the loadRecord method loads the selected row from the grid
in to a form. On clicking Save, the data is read from the
form and Ext.encode invoked with the data as parameter.


The code where the problem code is:



xtype: 'button',
text: 'Save',
handler: function() {
var rec = this.up('form').getRecord();
var jsonStr = Ext.encode(rec); // error occurs here
// this.up('form').up('window').close();
}


Note that if I replace getRecord with getValues(), the error does
Not occur.
If I replace getRecord with getFieldValues(), the error does occur.


Would you know why this error is occurring?
Thank you.

mschwartz
3 Jan 2012, 1:10 PM
Set a breakpoint at the offending line and examine the object you're trying to encode.

It's possible you have a circular kind of reference going on.

simonr25
3 Jan 2012, 2:16 PM
Hello,
Thank you for your reply.
I placed a breakpoint though this hasn't enabled me to resolve the problem
(the complete is below):


when I use getRecord()...
rec: Ext.Class.newClass
data: Object
...
raw: Object
descr: "US"
id: 2


When I use getValues()...
rec: Object
descr: "US"
id: "2"


Ext.define('Instance', {
extend: 'Ext.data.Model',


fields: [
{name: 'id', type: 'long'},
{name: 'descr', type: 'string'}
]
});




Ext.define('BILL.store.Instances', {
extend: 'Ext.data.Store',
model: 'Instance',
id: 'instances-store',
autoLoad: true,

proxy: {
type: 'ajax',


api: {
read: '/rating/listtst',


},
reader: {
type: 'json',
root: 'insts',
totalProperty: 'totrecs',
successProperty: 'success'
}
}
});




Ext.define('BILL.view.ListInstancesPage' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.instancespage',
id: 'instances-page',
selType: 'rowmodel',
store: 'Instances',


bbar: [
{
xtype: 'button',
text : 'Edit',
width: 100,
handler: function() {
var f = Ext.create('BILL.view.EditInstForm');
var w = Ext.create('BILL.view.CrudWin');
w.add(f);
var sel = Ext.getCmp('instances-page').getSelectionModel();

if (!sel.hasSelection())


alert('Record not Selected');
else {
Ext.getCmp('editinstfrm').loadRecord(sel.getSelection()[0]);
w.show();

}
}
},


],

initComponent: function() {


this.columns = [
{header: 'Id', dataIndex: 'id', hidden:false, flex: 1},
{header: 'Descr', dataIndex: 'descr', flex: 1},
];


this.callParent(arguments);
}

});




Ext.define('BILL.view.EditInstForm', {
extend: 'Ext.form.Panel',
alias: 'widget.editinstfrm',
id : 'editinstfrm',
frame: true,
bodyPadding: 5,

fieldDefaults: {
labelAlign: 'left',
msgTarget: 'side'
},


items: [
{
margin: '0 0 0 10',
xtype: 'fieldset',
defaults: {
width: 240,
labelWidth: 90
},
defaultType: 'textfield',

items: [{
fieldLabel: 'Id',
name: 'id',
readOnly: true,
xtype: 'numberfield',
allowDecimals: false
},{
fieldLabel: 'Descr',
name: 'descr',
}
]
},
{
xtype: 'button',
text: 'Save',
id: 'crudSaveBtn',
width: 90,
handler: function() {
var rec = this.up('form').getRecord();
var jsonStr = Ext.encode(rec);
this.up('form').up('window').close();
}
},
{
xtype: 'button',
text: 'Cancel',
id: 'crudCancelBtn',
width: 90,
handler: function() {
this.up('form').up('window').close();


}
}
]
}),


Ext.define('BILL.view.CrudWin', {
extend: 'Ext.window.Window',
width:220,
height: 200,
id: 'crudwin',
constrainHeader: true,
closable: false,
resizeable: true,
layout : 'fit',


defaults : {
frame : true
},


initComponent: function() {

this.callParent();
}
});



Ext.define('BILL.view.Window', {
extend: 'Ext.window.Window',
width:100,
height: 200,
constrainHeader: true,
title: 'Test',
layout : 'border',


defaults : {
frame : true,
split : true
},


initComponent: function() {


this.items = [


{
region : 'center',
xtype : 'instancespage',
height: 200


}
]
this.callParent();
}
});






Ext.application({
name: 'BILL',


appFolder: 'public/ratingmgr',

autoCreateViewport: false,


views: ['ListInstancesPage','CrudWin','EditInstForm'],
models: ['Instance'],
stores: ['Instances'],

launch: function() {


var v = Ext.create('BILL.view.Window').show();


}
});


data returned from controller:
{"totrecs":3,"insts": [{"id":1,"descr":"UK"},{"id":2,"descr":"US"},{"id":3,"descr":"France"}]}

skirtle
3 Jan 2012, 11:09 PM
Trying to Ext.encode a record is very unlikely to work. Same goes for instances of other ExtJS classes. There'll be all sorts of circular references and non-encodable property values.

A record is a complex class, it's not just a simple map of key to values.

If you're looking to send the contents of a record to your server then look into using a writer, such as Json writer.

simonr25
4 Jan 2012, 12:10 AM
Hello,

Thank you for your reply.
Although not shown in the code I have been using JSON writer.

I require to retrieve the data from the fields in a form panel into a model instance
that can then be sent using the json writer. I have tried getRecord and getFieldValues
both of which failed - getValues works but retrieves all fields as strings.

kind regards...

skirtle
4 Jan 2012, 12:32 AM
I require to retrieve the data from the fields in a form panel into a model instance that can then be sent using the json writer. I have tried getRecord and getFieldValues both of which failed - getValues works but retrieves all fields as strings.

The role of the writer is to convert the record to JSON. You shouldn't be using Ext.encode yourself. Just configure the writer on the model then call the save method.

The alternatives you discuss (getFieldValues and getValues) are both viable ways to achieve the desired result but you wouldn't go via a model and writer in that case, you'd just construct the Ajax request manually. Usually you wouldn't use Ext.encode with this technique either, you'd just specify the fields as request parameters. If you wanted to JSON encode the data then you'd do it in conjunction with the Ajax request's jsonData option. Off the top of my head I can't think of any reason why this wouldn't work with getFieldValues, so it may be worth digging in deeper to understand why you're getting an error in that case. Try removing some of your fields to see which ones cause the problem.

simonr25
4 Jan 2012, 1:30 AM
Many thanks for your reply.
The remaining issues I believe I have are as follows:


1) If I use save() on a model instance, how do I distinguish between creation of a new record or update of an existing record? I could add a new field (e.g. newrecord) in the record (model) to be sent, but this could cause conflict with the controller since it decodes the json request into a json object - the object doesn't contain this field.


2) These records are also displayed in a grid - so I could use sync on a store. The problems with this is that I don't believe there's a sync callback to tell if database update was successful? - secondly even if there was a way of telling if the sync was successful, if the controller sends a failure response I do not know how/if it's possible to revert the store to its contents prior to the sync? Would you know about these?


3) I believe in Ext JS 3 there were the following store methods:
getModifiedRecords(), rejectChanges(), commitChanges()...


I haven't been able to find these in the Ext JS 4 documentation.
Would you know if they still exist?


kind regards,

skirtle
4 Jan 2012, 3:18 PM
You're much more likely to get help if you post separate questions as separate threads. Short questions like the three you posted will usually get very prompt responses if you post them separately. Tagging all three onto the end of this thread means that most people won't read them.

I'll attempt an answer at the first one.


1) If I use save() on a model instance, how do I distinguish between creation of a new record or update of an existing record? I could add a new field (e.g. newrecord) in the record (model) to be sent, but this could cause conflict with the controller since it decodes the json request into a json object - the object doesn't contain this field.

Existing records would have an id, new records won't?

You could configure different server URLs for create and update if necessary:

http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.proxy.Ajax-cfg-api