PDA

View Full Version : After create/close some Ext.FormPanel FF got huge memory



alwayscy
13 Sep 2009, 10:25 PM
I extend FormPanel something like:
Ext.extend(eb.config.BaseConfForm, Ext.FormPanel, {...

create it:
var form = new eb.config.BaseConfForm({
panels : panels,
objName : eb.Const.objTypeName[objtype],
nodeAttr : nodeAttr,
crudMode : crudMode,
easyMode : easymode
});

and add it to a Ext.TabPanel, use can use close button of TabPanel to close it.

My app need create many object using different FormPanel, after do tens of creation, FF memory will go up to 200M+. Some object creation form have 30+ field organized in TabPanel.

Do I miss some free/release operations?

Thanks!

16182

Condor
13 Sep 2009, 10:52 PM
The taskmanager it not a good method to check for memory leaks. sIEve is much better for that.

You don't show anything about eb.config.BaseConfForm, so I can't say if you are leaking component or not.

alwayscy
14 Sep 2009, 5:07 AM
I use sIEve, it seemed many orphan are fields of form.
I try to add delete code in outside TabPanel remove event handler, not work.


Sorry for very long code, when I coding, I dosen't even know about JS leak problem.


/**
* a base form for normal looking of GUI
*
* @include "/ebnms/src/main/webapp/js/eb/common/Const.js"
* @include "/ebnms/src/main/webapp/js/eb/config/Const.js"
*/

/**
* A reusable error reader class for XML forms
*/
Ext.form.XmlErrorReader = function() {
Ext.form.XmlErrorReader.superclass.constructor.call(this, {
record : 'field',
success : '@success'
}, ['id', 'msg']);
};

/**
* a util function for string class have format ability
*/
String.prototype.format = function() {
var str = this;

for (var i = 0; i < arguments.length; i++) {
var re = new RegExp('\\{' + (i) + '\\}', 'gm');
str = str.replace(re, arguments[i]);
}

return str;
}

/**
* a trim function
*/
String.prototype.trim = function() {
return this.replace(/^\s*/, "").replace(/\s*$/, "");
}

Ext.extend(Ext.form.XmlErrorReader, Ext.data.XmlReader, {
readRecords : function(doc) {
var ret = Ext.form.XmlErrorReader.superclass.readRecords.call(
this, doc);

// translate remote msg id to my local string
for (var i = 0; i < ret.records.length; i++) {
var r = ret.records[i].data;

// maybe have parameters need translate
var arr = r.msg.split(';');
if (arr.length > 0) {
if (!eb.Util.isUndef(eb.ErrMsg[arr[0]])) {
if (1 == arr.length) {
// just a msg id
r.msg = eb.ErrMsg[arr[0]];
} else if (2 == arr.length) {
// msg id with parameters
r.msg = eb.ErrMsg[arr[0]].format.apply(
eb.ErrMsg[arr[0]], arr[1].split(','));
}
}
}
}
return ret;
}
});

/**
* a translate reader class for node value to display value NO USE NOW
*/
eb.config.NodeValueReader = function(meta, recordType) {
eb.config.NodeValueReader.superclass.constructor.apply(this, arguments);
};

Ext.extend(eb.config.NodeValueReader, Ext.data.XmlReader, {
readRecords : function(doc) {
var ret = eb.config.NodeValueReader.superclass.readRecords
.call(this, doc);

// translate remote value
for (var i = 0; i < ret.records.length; i++) {
var r = ret.records[i].data;
this.fs.nodeValue2Dispaly.call(this.fs, r);
}
return ret;
}
});

/**
* a base form for normal looking of GUI
*
* @param nodeAttr
* attributes of tree node being clicked on, try to load it's data
* first
*/
eb.config.BaseConfForm = function(config) {
// create title
var title;
var submitUrl;
switch (config.crudMode) {
case eb.Const.operType.create :
title = eb.Const.menuName.create;
submitUrl = eb.Const.serverUrl.createObject;
break;

case eb.Const.operType.read :
title = eb.Const.menuName.read;
submitUrl = eb.Const.serverUrl.readObject;
break;

case eb.Const.operType.update :
title = eb.Const.menuName.update;
submitUrl = eb.Const.serverUrl.updateObject;
break;

case eb.Const.operType.del :
title = eb.Const.menuName.del;
submitUrl = eb.Const.serverUrl.deleteObject;
break;
}

title += "-" + config.objName;

this.objName = config.objName;

// create tab panel
// for set id of components
var objIdentify = config.nodeAttr.obj_type + '_' + config.nodeAttr.obj_id;
eb.log.debug(title + "for:" + objIdentify);
var panels = [];
for (var i = 0; i < config.panels.length; i++) {
panels.push(this.addPanelTab(objIdentify, config.panels[i]));
}

this.preConfigPanels = config.panels; // store for later use

// apply to Form inside FormPanel
Ext.apply(config, {
id : eb.config.BaseConfForm.getNodeId(config.crudMode, config.nodeAttr),
title : title,
errorReader : new Ext.form.XmlErrorReader(),

reader : new eb.config.NodeValueReader({
record : 'object',// config.objName,
success : '@success'
}, this.myReader),

// autoScroll : true,
// autoHeight : true,

items : {
xtype : 'tabpanel',
enableTabScroll : true,
// layoutOnTabChange : true,
border : false,
// autoScroll : true,
// autoHeight : true,
defaults : {
// autoHeight : true,
// bodyStyle : 'padding:10px'

autoScroll : true
// as we use deferredRender:false we mustn't
// render tabs into display:none containers
// hideMode : 'offsets'
},

// buttonAlign : 'left',
//
// buttons : [{
// text : 'load'
// },{
// text : 'save'
// }],

// for load everything when initing
deferredRender : true,
activeTab : 0,
items : panels,
fs : this,
listeners : {
tabchange : function(panel) {
this.fs.handleInheritChange.call(this.fs);

// because tab pannel render later, need translate value on first active
this.fs.hideValue2Display.call(this.fs, this.fs.getForm());
}
// ,
// bodyresize : function(panel, w, h) {
// eb.log.debug("form tab resize w:" + panel.getInnerWidth()
// + ", h:" + panel.getInnerHeight());
// }
}
},

baseParams : {
// monitor_id : config.nodeAttr.monitor_id,
// obj_type : config.nodeAttr.obj_type
}
});

// some parameters submit to server
for (key in config.nodeAttr) {
config.baseParams[key] = config.nodeAttr[key];
this.baseParams[key] = config.nodeAttr[key]; // save a copy
}

if (config.crudMode != eb.Const.operType.create) {
// create operation, no need this field
// config.baseParams.obj_id = config.nodeAttr.obj_id;

this.objId = config.nodeAttr.obj_id;
}

// let reader can invoke my function
config.reader.fs = this;

eb.config.BaseConfForm.superclass.constructor.call(this, config);

// add custom event
// this.addEvents('submitcompleted');

var fs = this.getForm();

// this.crudMode = config.crudMode;

if (config.crudMode != eb.Const.operType.create) {
// add load button
this.addButton(eb.Const.buttonName.load, function() {
fs.load({
url : eb.Const.serverUrl.readObject,
waitMsg : eb.Const.msgWait.loading
});
});
}

// add submit button
this.submitBtn = this.addButton({
text : eb.Const.buttonName.submit,
disabled : (config.crudMode == eb.Const.operType.create) ? false : true,
handler : function() {
var dirt = fs.isDirty();

if (dirt) {
if (!fs.isValid()) {
Ext.MessageBox.alert('Errors',
'Please fix the errors noted.');
return;
}

Ext.Msg.show({
title : 'Save Changes?',
msg : 'There could be unsaved changes. <br/>Would you like to save your changes?',
buttons : Ext.Msg.YESNOCANCEL,
fn : function(button) {
switch (button) {
case 'yes' :
fs.submit({
url : submitUrl,
waitMsg : eb.Const.msgWait.saving
})
break;
case 'no' :
break;
}
},
icon : Ext.MessageBox.QUESTION
});
} else {
Ext.MessageBox.alert('Info', 'Nothing To Submit!');
}
}
});
};

Ext.extend(eb.config.BaseConfForm, Ext.FormPanel, {
labelAlign : 'right',
buttonAlign : 'left',
waitMsgTarget : true,
width : 620,

/**
* some value need be locale
*/
inheritFormat : "Inherit from {0}",

/**
* panel will add their reader to this array
*/
myReader : [],

/**
* object id editing now, if is new operation, objId = 0
*/
objId : 0,

objName : "",

/**
* base parameters pass to this form. e.g.: objid, objtype
*
* @type {}
*/
baseParams : {},

/**
* store pre config items
*/
preConfigPanels : null,

closable : true,

listeners : {
// scope : this,

bodyresize : function(panel, w, h) {
eb.log.debug("form resize w:" + panel.getInnerWidth() + ", h:"
+ panel.getInnerHeight());
var tab = panel.getComponent(0);
tab.setSize(panel.getInnerWidth(), panel.getInnerHeight());
},

beforeaction : function(f, a) {
if (a.type == 'submit') {
// do trans work
// this.displayValue2Node ();

// a.options.params = a.options.params || {};
// Ext.apply(a.options.params, {common_is_volatile: '1'});

/**
* before submit I'll change checkbox value according to inherit
* relation
*/
this.handleInheritChange();
}
},

actioncomplete : function(f, a) {
if (a.type == 'load') {
this.submitBtn.enable();

// Optional. Just for testing
// Ext.MessageBox.alert('fs.actioncomplete', 'All OK.');
this.hideValue2Display(f);

// change inherited properties according to value from server
this.handleInheritChange();
} else if (a.type == 'submit') {
// If the responseText is a null string, Ext doesnt
// raise an error so trap it here
// as an error because it should be some json.
if (a.response.responseText == '') {
Ext.MessageBox
.alert('fs.actioncomplete error',
'Form submit returned an empty string instead of json');
} else {
// just for testing
// Ext.MessageBox.alert('fs.actioncomplete', 'All OK.'); //
// Optional.
if (this.crudMode == eb.Const.operType.update) {
// if update succ, load again
// this.loadObj();

// for service form notify host father its close event
// this.fireEvent('submitcompleted', '998');

// notify all handler connect to public bus
eb.BusManager.sendEvent(eb.Const.compType.config
+ this.initialConfig.crudMode,
this.initialConfig.nodeAttr.obj_type,
this.initialConfig.nodeAttr.monitor_id,
this.initialConfig.nodeAttr.obj_id);
}
}
}
},

/**
* display error msg when failed
*/
actionfailed : function(form, action) {
// alert('actionfailed');
if (action.type == 'load') {
// Handle the LOAD errors
if (action.failureType == "connect") {
Ext.MessageBox.alert('fs.actionfailed error',
'Form load failed. Could not connect to server.');
} else {
if (action.response.responseText != '') {
var result = Ext.decode(action.response.responseText);
if (result && result.msg) {
Ext.MessageBox.alert('fs.actionfailed error',
'Form load failed with error: '
+ action.result.msg);
} else {
Ext.MessageBox
.alert(
'fs.actionfailed error',
'Form load failed with unknown error (possibly missing the "success" field in the json). Action type='
+ action.type
+ ', failure type='
+ action.failureType);
}
} else {
Ext.MessageBox
.alert('fs.actionfailed error',
'Form load returned an empty string instead of json');
}
}
} else if (action.type == 'submit') {
// Handle the SUBMIT errors

if (action.failureType == "connect") {
Ext.MessageBox.alert('fs.actionfailed error',
'Form submit failed. Could not connect to server.');
} else if (action.failureType == "server") {
// These arent "errors" as such, they are validation
// issues trapped by the server script and passed
// back for the user to correct
} else {
var result = Ext.decode(action.response.responseText);
if (result && result.msg) {
Ext.MessageBox.alert('fs.actionfailed error',
'Form submit failed with error: '
+ action.result.msg);
} else {
Ext.MessageBox.alert('actionfailed Error',
'Form submit returned unknown error. Action type='
+ action.type + ', failure type='
+ action.failureType);
}
}
}
},

/**
* auto load data
*/
render : function() {

// eb.config.BaseConfForm.superclass.render.apply(this, arguments);

// auto load only when not CREATE operation
eb.log.debug("render base form");
if (this.crudMode != eb.Const.operType.create) {
this.loadObj();
}

this.dropZone = new Ext.dd.DropZone(this.el, {
ddGroup : 'test'
});
}
}, // listeners end

/**
* load data
*/
loadObj : function() {
var fs = this.getForm();
fs.load({
url : eb.Const.serverUrl.readObject,
waitMsg : eb.Const.msgWait.loading
});
},

/**
* change the hide DB value to display area of GUI after load complete or tab changed
*/
hideValue2Display : function(f) {
var father = this;
f.items.each(function(item) {
var type = father.ebTypeOfItem(item.name);
var dfield = f.findField("display_" + item.name);

// tab pannel is lazy rendered, we need check if this field is created?
if (dfield != null && dfield.rendered) {
if (type === eb.Const.ebType.setEdit) {
father.setListDisplayValue(item.name, father.objId);
} else if (type === eb.Const.ebType.multiselect) {
var field = f.findField(item.name);
var displayvalue = MultiSelectBox
.formatDisplayStr(field.getValue());
dfield.setValue(displayvalue);
eb.log.debug("set multi " + item.name + " to "
+ displayvalue);
}
}
});
},

/**
* a hide list box field value to list box display value
*
* @param {}
* name
* @param {}
* objId
*/
setListDisplayValue : function(name, objId) {
var f = this.getForm();
var field = f.findField(name);
var dfield = f.findField("display_" + name);
var s = dfield.view.store;
s.removeAll();

var ret = EditSetBox.formatJSONDisplayObj(field.getValue(), objId);

s.loadData(ret);

dfield.view.refresh();

eb.log.debug("set list " + name + " to " + ret);
},

/**
* node value to display value NO USE NOW, we use fieldValue in combo for
* set value automaticly
*
* @param r
* record from xml reader
*/
nodeValue2Dispaly : function(r) {
var ebtype = eb.Const.ebType;
var cc = eb.Const;
var father = this;
father.getForm().items.each(function(item) {
// value need be trans
var v = r[item.name];
if (Ext.type(v) == false)
return;

v = v.trim();

if (v.length > 0 && !eb.Util.isUndef(item.xtype))
if (item.xtype === 'statictextfield') {
// reformat inherit from string
r[item.name] = father.inheritFormat.format(v);
eb.log.debug("trans to:" + r[item.name]);
}
// else if (item.xtype === 'itemselector') {
// item.myvalue = v;
// father.initMultiSelect(item);
// }
else if (item.xtype === 'combo') {
if (!eb.Util.isUndef(item.ebtype)
&& item.ebtype == ebtype.urlComb) {
// item.myvalue = v;
// father.initCombo(item);
item.setValue(v);
}
}
});
},

/**
* get the ebtype of specific item
*/
ebTypeOfItem : function(name) {
for (var i = 0; i < this.preConfigPanels.length; i++) {
for (var j = 0; j < this.preConfigPanels[i].items.length; j++) {
var item = this.preConfigPanels[i].items[j];
if (item.name === name) {
if (eb.Util.isDef(item.ebtype))
return item.ebtype;
else
return "";
}
}
}

return "";
},

/**
* init multiselect value<br>
* only when value and FROM list loaded, can execute this function
*/
initMultiSelect : function(item) {
if (eb.Util.isUndef(item.myvalue) || eb.Util.isUndef(item.fromReady)) {
eb.log.debug("multiselect data not ready:" + item.name);
return;
}

var v = item.myvalue;
var i, j;
var from = item.fromMultiselect.view.store;
var to = item.toMultiselect.view.store;

// move the TO list back to FROM list
while (to.getCount() > 0) {
var r1 = to.getAt(0);
from.add(r1);
to.remove(r1);
}

// move the FROM list item to TO list according to received data
var arr = v.split(',');
for (j = 0; j < arr.length; j++) {
var testv = arr[j].trim();

for (i = 0; i < from.getCount(); i++) {
var r1 = from.getAt(i);
if (r1.data.value == testv) {
to.add(r1);
from.remove(r1);
break;
}
}
}

// refresh GUI
item.toMultiselect.view.refresh();
item.fromMultiselect.view.refresh();
},

/**
* init combo value<br>
* only when value and combo loaded, can execute this function
*/
initCombo : function(item) {
if (eb.Util.isUndef(item.myvalue) || eb.Util.isUndef(item.comboReady)) {
eb.log.debug("combo data not ready:" + item.name);
return;
}

item.setValue(item.myvalue);
},

/**
* format some value before submit to node NO USE NOW, we use hiddenName for
* sumbit value
*/
displayValue2Node : function() {
var ebtype = eb.Const.ebType;
var cc = eb.Const;
this.getForm().items.each(function(item) {
// value need be trans
var v = item.value;

if (!eb.Util.isUndef(item.ebtype)) {
/*
* if (item.ebtype === ebtype.abilityComb) { if (v ===
* cc.abilityComb.enable) item.setRawValue ('1'); else
* if (v === cc.abilityComb.disable) item.setRawValue
* ('0'); } else if (item.ebtype ===
* ebtype.yesOrNoComb){ if (v === cc.yesOrNoComb.yes)
* item.setRawValue ('1'); else if (v ===
* cc.yesOrNoComb.no) item.setRawValue ('0'); }
*/
/*
* if (item.ebtype === ebtype.yesOrNoComb || item.ebtype
* === ebtype.abilityComb) { if (typeof item.el !==
* 'undefined') { var r =
* item.findRecord(item.displayField,
* item.lastSelectionText); if (typeof r !==
* 'undefined') { item.setRawValue
* (r.data[item.valueField]); } } }
*/

eb.log.debug("trans to:" + item.value);
}
});
},

/**
* add panel through pre-defined fields we can group common fields into a
* panel and reuse it
*/
addPanelTab : function(objIdentify, panelPar) {
var form = {
title : panelPar.title,
defaults : {
// bodyStyle : 'padding:2px'
},

layout : 'absolute',
renderHidden : true,
items : []
};

var posx = 20, posy = 20, lineHeight = 28, checkwidth = 15, fieldwidth = 280;
var editsetheight = lineHeight * 4, editsetwidth = 400;
var buttonwidth = 45;

// form.height = posy + (panelPar.items.length + 1) * lineHeight;
var templatename = "common_template_use";
// Ext.util.Format.lowercase(this.objName + '_templates');

for (var i = 0; i < panelPar.items.length; i++) {
var item = panelPar.items[i];

item.listeners = {};

// make xml reader for this item
this.myReader.push(item.name);

/**
* add checkbox, only need it in inheritance relation
*/
if (typeof item.inheritable !== eb.Const.undef
&& item.inheritable == true) {
this.addInheritedThings(form, item, objIdentify, posx, posy,
checkwidth, fieldwidth);
}

/**
* handle hidden field
*/
if (item.ebtype === eb.Const.ebType.hidden) {
item.id = "id_" + objIdentify + '_' + item.name;
item.xtype = 'hidden';
form.items.push(item);
eb.log.debug("add hidden:" + item.id);
continue;
}

/**
* add field
*/
var fieldpanel = this.addEbField(form, item, objIdentify, posx,
posy, checkwidth, fieldwidth);

/**
* add drop zone if needed, see addDropZone function
*/
// addDropZone ();
/**
* add a button for trigger multiselect box
*/
if (typeof item.ebtype !== eb.Const.undef
&& item.ebtype === eb.Const.ebType.multiselect) {

item.width += editsetwidth - fieldwidth;
fieldpanel.width += editsetwidth - fieldwidth;

this.addMultiselectThings(form, item, objIdentify, fieldpanel,
fieldpanel.x + fieldpanel.width, posy, buttonwidth);

// set text area height
fieldpanel.items[0].height = editsetheight;
posy += fieldpanel.items[0].height - lineHeight + 6;
}

/**
* add button for edit set properties, e.g. services, hostgroups
*/
if (typeof item.ebtype !== eb.Const.undef
&& item.ebtype === eb.Const.ebType.setEdit) {

item.width += editsetwidth - fieldwidth;
fieldpanel.width += editsetwidth - fieldwidth;

this.addSetEditThings(form, item, objIdentify, fieldpanel,
fieldpanel.x + fieldpanel.width, posy, buttonwidth);

// set text area height
fieldpanel.items[0].height = editsetheight;
eb.log.debug("add set edit: " + fieldpanel.items[0].id
+ ", height:" + fieldpanel.items[0].height);
posy += fieldpanel.items[0].height - lineHeight + 6;
}

/**
* add inheritance trigger for change checkbox and component status
*/
// eb.log.debug ("test name:"+fieldpanel.items[0].name);
if (fieldpanel.items[0].name.indexOf(templatename) != -1) {
eb.log
.debug("add template trigger:"
+ fieldpanel.items[0].name);

Ext.apply(fieldpanel.items[0], {
listeners : {
change : {
fn : function() {
eb.log.debug("template change");
this.handleInheritChange();
},
scope : this
}
}
})
}

posy += lineHeight;
}

form.height = posy;

// this.tabItems.push(form);

return form;
},

/**
* add field to form
*
* @param form
* a json definition segment for this form
* @param item
* eb item definition
* @param objIdentify
* editing object identify
* @param posx
* x pos of this button
* @param posy
* y pos of this button
* @param checkwidth
* width of checkbox
* @param fieldwidth
* width of field
*/
addEbField : function(form, item, objIdentify, posx, posy, checkwidth,
fieldwidth) {
var fieldpanel = {
labelAlign : 'left',
labelWidth : 100,
width : fieldwidth,
defaults : {
// applied to each contained item
// width : fieldwidth - 130
// anchor:'95%'
},
// xtype : 'panel',
layout : 'form',
border : false,
x : posx + checkwidth,
y : posy,

items : []
}

item.width = fieldwidth - 130;

item.id = "id_" + objIdentify + '_' + item.name;
// eb.log.debug ("add item:"+item.id);

// avoid handle more than one time
// if (eb.Util.isUndef(item.labelStyle))
{
// create some special xtype for me
this.handleItemXtype(item);

item.labelStyle = 'cursor: help;';
item.name = item.name;

// add tooltip
item.listeners.render = eb.Util.setFormFieldTooltip;
}

fieldpanel.items.push(item);
form.items.push(fieldpanel);

return fieldpanel;
},

/**
* add a inherited checkbox and inherited from text at the end of field
*
* @param form
* a json definition segment for this form
* @param item
* eb item definition
* @param objIdentify
* editing object identify
* @param posx
* x pos of this button
* @param posy
* y pos of this button
* @param checkwidth
* width of checkbox
* @param fieldwidth
* width of field
*/
addInheritedThings : function(form, item, objIdentify, posx, posy,
checkwidth, fieldwidth) {
/*
* var checkpanel = { xtype : 'panel', layout : 'form', labelAlign :
* 'left', columnWidth : .18, labelWidth : 55, border : false, items : [] };
*/

var checkitem = {
xtype : 'checkbox',
inputValue : '1',
// labelSeparator : '',
// fieldLabel : '',
// boxLabel : 'Overright',
// cls : 'x-hide-display',
disabled : true,
hidden : true,
x : posx,
y : posy + 3,
listeners : {
scope : this,

/**
* disable/enable value components
*/
check : function(me, isChecked) {
// eb.log.debug(me.name + ": " + isChecked);
var f = this.getForm();

// get name of component, skip string 'checkbox_'
var comp = f.findField(me.name.substr(9));
if (comp != null) {
if (isChecked)
comp.enable();
else
comp.disable();
}
}
}
};
checkitem.name = 'checkbox_' + item.name;
checkitem.id = 'checkid_' + objIdentify + '_' + item.name;

// let checkbox auto load data effect
this.myReader.push(checkitem.name);

// checkpanel.items.push(checkitem);
// line.items.push(checkpanel);

form.items.push(checkitem);

/**
* add inherit from static text field
*/
var statictext = {
name : "inheritfrom_" + item.name,
xtype : 'statictextfield',
width : 150,
x : posx + checkwidth + fieldwidth + 30,
y : posy
};

form.items.push(statictext);

this.myReader.push(statictext.name);
},

/**
* add a multiselect browser button and hidden value item
*
* @param form
* a json definition segment for this form
* @param item
* eb item definition
* @param posx
* x pos of this button
* @param posy
* y pos of this button
* @param buttonwidth
* width of button
*/
addMultiselectThings : function(form, item, objIdentify, fieldpanel, posx,
posy, buttonwidth) {
eb.log.debug(item.ebUrl);

// add a new component for submit value
var displayValue = {
xtype : "textarea",
name : "display_" + item.name,
id : "seteditid_" + objIdentify + '_' + item.name,
readOnly : true
};

Ext.applyIf(displayValue, item);

// add hidden item to form for submit
item.xtype = "hidden";

// avoid hidden component exception for tooltip
delete item.listeners;
form.items.push(item);

// pop item from display panel, and replace it with textarea
fieldpanel.items.pop();
fieldpanel.items.push(displayValue);

var father = this;
var boxbutton = {
border : false,
layout : 'form',
x : posx,
y : posy,
width : buttonwidth + 5,
items : [{
width : buttonwidth,
xtype : 'button',
text : '...',
ebItem : item,
listeners : {
click : function(me, e) {
eb.log.debug(me.ebItem.name + "/" + me.ebItem.ebUrl);

// get field and its value
var frm = father.getForm();
var f = frm.findField(me.ebItem.name);
var myvalue = '';
if (f != null)
myvalue = f.getValue();

// add monitor par to url
var url = me.ebItem.ebUrl;
if (!eb.Util.isUndef(frm.baseParams.monitor_id))
url += "&monitor_id=" + frm.baseParams.monitor_id;

MultiSelectBox.showBox({
ebUrl : url,
resultAsArray : true,
initValue : myvalue,
callback : {
func : function(v) {
// set real value
f.setValue(v);
eb.log.debug("set multi "
+ f.getName() + " to " + v);

// update display textarea
var dfield = frm
.findField("display_"
+ me.ebItem.name);
dfield.setValue(MultiSelectBox
.formatDisplayStr(v));
dfield.fireEvent('change');
},
scope : f
}
});
}
}
}]
};

form.items.push(boxbutton);
},

/**
* add button and hide value for edit set properties, e.g. services,
* hostgroups
*
* @param form
* a json definition segment for this form
* @param item
* eb item definition
* @param posx
* x pos of this button
* @param posy
* y pos of this button
* @param buttonwidth
* width of button
*/
addSetEditThings : function(form, item, objIdentify, fieldpanel, posx,
posy, buttonwidth) {
var father = this;

/**
* add a new component for submit value FIX: we need comment
* Multiselect.js' line like this:
* if(!this.legend)fs.el.down('.'+fs.headerCls).remove();
*/
// use textarea
// var displayValue = {
// xtype : "textarea",
// name : "display_" + item.name,
// id : "seteditid_" + objIdentify + '_' + item.name,
// readOnly : true
// };
// use multiselect
var displayValue = {
xtype : 'multiselect',
// fieldLabel : 'hehe2',
// name : "multiselectbox",
name : "display_" + item.name,
id : "seteditid_" + objIdentify + '_' + item.name,
readOnly : true,

// isFormField : false,

// dataFields : ['value', 'display'],
// data : [['1', 'One'], ['2', 'Two'], ['3', 'Three'], ['4',
// 'Four'],
// ['5', 'Five']],
store : new Ext.data.JsonStore({
data : [],
fields : ['value', 'display']
}),

// width : 320,
// anchor : '100%', // anchor width by percentage
// msWidth : fieldpanel,
// msHeight : 350,
// anchor : '100% -53', // anchor width by percentage

imagePath : "/ebnms/images/MutiSelect",
// legend : "Multiselect",

valueField : 'value',
displayField : 'display'
};

Ext.applyIf(displayValue, item);

// eb.log.debug ("add set edit: "+displayValue.name+",
// height:"+displayValue.height);

// add hidden item to form for submit
item.xtype = "hidden";
delete item.listeners; // avoid hidden component exception for tooltip
form.items.push(item);

// pop item from display panel, and replace it with textarea
fieldpanel.items.pop();
fieldpanel.items.push(displayValue);

/**
* add an add button
*/
var boxbutton = {
border : false,
layout : 'form',
x : posx,
y : posy,
width : buttonwidth + 5,
items : [{
width : buttonwidth,
xtype : 'button',
name : "button_" + item.name,
text : '...',
ebItem : item,
listeners : {
click : function(me, e) {
eb.log.debug(me.ebItem.name + "/" + me.ebItem.ebUrl);

var frm = father.getForm();
var f = frm.findField(me.ebItem.name);
var myvalue = '';
if (f != null)
myvalue = f.getValue();

// add monitor par to url
var url = me.ebItem.ebUrl;
if (!eb.Util.isUndef(frm.baseParams.monitor_id))
url += "&monitor_id=" + frm.baseParams.monitor_id;

var dialog = new EditSetBox({
initValue : myvalue,
addUrl : url,
objId : father.objId,
callback : {
func : function(v) {
// set real submit value and update
// display
// value
f.setValue(v);
father.setListDisplayValue(
me.ebItem.name,
father.objId);
},
scope : f
}
});

dialog.showBox();
}
}
}]
};

form.items.push(boxbutton);

/**
* add an edit button
*/
if (typeof item.ebEditType !== eb.Const.undef) {
var editbutton = {
border : false,
layout : 'form',
x : posx,
y : posy + 30,
width : buttonwidth + 5,
items : [{
width : buttonwidth,
xtype : 'button',
text : 'edit',
ebItem : item,
listeners : {
click : function(me, e) {
// add edit tab
father.editSetItem(item);
}
}
}]
};

form.items.push(editbutton);
}
},

/**
* edit set item
*/
editSetItem : function(item) {
eb.log.debug(item.name + "/" + item.ebUrl);

// get value
var frm = this.getForm();
var f = frm.findField("display_" + item.name);
var myvalue = '';
if (f != null)
myvalue = f.getValue();

// add monitor par to url
var url = item.ebUrl;
if (!eb.Util.isUndef(frm.baseParams.monitor_id))
url += "&monitor_id=" + frm.baseParams.monitor_id;

eb.log.debug("begin edit monitor:" + frm.baseParams.monitor_id
+ " objid:" + myvalue + " parentid:" + this.objId);

// add tab
eb.BusManager.doCommand(eb.Const.compType.config,
eb.Const.operType.update, item.ebEditType,
frm.baseParams.monitor_id, myvalue, {
obj_parent : this.objId
});

// handle close event
var fn = function(objtype, monitorid, objid) {
if (objtype === item.ebEditType
&& monitorid === frm.baseParams.monitor_id
&& objid === myvalue) {
eb.log.debug("detect " + objtype + " edit tab close id "
+ objid);

// reload this edit set box
this.reloadSetItems(item.name);
}
};

eb.BusManager.addEventHandler(eb.Const.compType.config
+ eb.Const.operType.update, fn, this);

// var form = eb.config.ServiceConf.createForm('u', {
// monitor_id : frm.baseParams.monitor_id,
// obj_parent : this.objId,
// obj_id : myvalue
// });
// eb.Main.addUpTab(form);

// var father = this;
// form.on('submitcompleted', function(id) {
// this.getIdentify();
// eb.log.debug("set edit closed:" + id);
// eb.Main.upTabs.remove(form);
// father.reloadSetItems(item.name);
// });
},

getIdentify : function() {
var ret = this.baseParams.obj_type + "_" + this.baseParams.obj_id;
eb.log.debug("I'm " + ret);
return ret;
},

/**
* reload set edit item after edit successful and form closed
*/
reloadSetItems : function(name) {

// Ext.MessageBox.alert("hehe", "good");
var father = this;

this.getIdentify();

var store = new Ext.data.Store({
proxy : new Ext.data.HttpProxy({
url : eb.Const.serverUrl.readObject,
method : 'POST'
}),
baseParams : this.baseParams,
// waitMsg : eb.Const.msgWait.loading,
reader : new Ext.data.XmlReader({
record : father.objName,
success : '@success'
}, father.myReader)
});

store.load({
callback : function(r, options, succ) {
if (succ) {
var d = r[0].data[name];
eb.log.debug("reload edit set data:" + d);

var f = this.getForm();
var field = f.findField(name);
field.setValue(d);
this.setListDisplayValue(name, this.objId);
}
},
scope : this
});

// Ext.Ajax.request({
// url : eb.Const.serverUrl.readObject,
// waitMsg : eb.Const.msgWait.loading,
// params : this.baseParams,
// failure : function(response, options) {
//
// Ext.MessageBox.alert('Warning', 'Oops...');
// },
//
// success : function(response, options) {
// // Ext.MessageBox.alert('Success','Yeah...');
// eb.log.debug(response);
//
// // skip header
// // var start = response.responseText.indexOf("<", 2);
// // var content = response.responseText.substring(start);
//
// // parse xml result
// var store = new Ext.data.Store({
// reader : new Ext.data.XmlReader({
// url : eb.Const.serverUrl.readObject,
// waitMsg : eb.Const.msgWait.loading,
// baseParams : this.baseParams,
// record : father.objName,
// success : '@success'
// }, father.myReader)
// });
//
// store.load();
//
// // store.loadData(response.responseText);
//
// store.getAt(0);
//
// // var data = Ext.util.JSON.decode();
//
// }
// });
},

addDropZone : function() {
/**
* add dropzone if needed
*/
// if (typeof item.ebDrag != 'undefined') {
// item.listeners = {
// render : function() {
// eb.log.info("add item drop");
//
// this.dropTarget = new Ext.dd.DropTarget(this.getEl(), {
// ddGroup : "ebConfigDD",
// copy : false,
// notifyDrop : function(dd, e, data) {
// var target = Ext.getCmp(e.target.id);
// var node = data.nodes[0];
// var attr = node.attributes;
// target.setValue(node.text);
// console.info('drop target ' + "id/type = "
// + attr.objId + "/" + attr.objType);
// return true;
// },
// notifyEnter : function(dd, e, data) {
// console.info('enter');
// return this.dropAllowed;
// },
// notifyOut : function(dd, e, data) {
// console.info('out');
// }
// });
// }
// };
// }
},

yesOrNoStore : new Ext.data.SimpleStore({
fields : ['display', 'value'],
data : [[eb.Const.combNullText, eb.Const.combNullValue],
[eb.Const.yesOrNoComb.yes, '1'], [eb.Const.yesOrNoComb.no, '0']]
}),

abilityStore : new Ext.data.SimpleStore({
fields : ['display', 'value'],
data : [[eb.Const.combNullText, eb.Const.combNullValue],
[eb.Const.abilityComb.enable, '1'],
[eb.Const.abilityComb.disable, '0']]
}),

stateStore : new Ext.data.SimpleStore({
fields : ['display', 'value'],
data : [[eb.Const.combNullText, eb.Const.combNullValue],
[eb.Const.stateComb.o, 'o'],
[eb.Const.stateComb.w, 'w'],
[eb.Const.stateComb.c, 'c'],
[eb.Const.stateComb.u, 'u']]
}),

/**
* enable or disable checkbox when template field change
*/
handleInheritChange : function() {
/**
* determine if we need show inheritance overright checkbox
*/
var f = this.getForm();
var templatename = "common_template_use";
// Ext.util.Format.lowercase(this.objName + '_templates');

var template = f.findField(templatename);
if (!template)
return;

// eb.log.debug("tabchange " + template.getValue() + "/"
// + template.getRawValue());

// eb.log.debug (template.getRawValue()+"/"+template.getValue ());
var bshow = true;
var value = template.getValue();
value = value.trim();
if (value === '' || value === null)
bshow = false;
else {
var tmp = Ext.util.JSON.decode(value);
if (tmp.length == 0)
bshow = false;
}

eb.log.debug(templatename + ": value is " + value + " and bshow: "
+ bshow);

function enableComp(f, name, bshow, checkvalue) {
var comp = f.findField(name);
if (bshow) {
// change the status of components manage by this checkbox
if (comp != null) {
if (checkvalue)
comp.enable();
else
comp.disable();
}
} else {
if (comp != null) {
comp.enable();
}
}
};

/**
* do show/off work with all checkbox
*/
f.items.each(function(item) {
if (item.name.indexOf('checkbox_') == 0) {
// if (typeof item.inheritable !== 'undefined'
// && item.inheritable == true) {

// handle checkbox
if (bshow) {
if (!item.isVisible()) {
item.setVisible(true);
item.enable();
// eb.log.debug ("enable check:"+item.name);
}
} else {
if (item.isVisible()) {
item.setVisible(false);
item.disable();
// eb.log.debug ("disable check:"+item.name);
}
}

// handle components
var checkvalue = item.getValue();
var realitemname = item.name.substr(9);
enableComp(f, realitemname, bshow, checkvalue);
// enableComp(f, "display_" + realitemname, bshow);
// enableComp(f, "button_" + realitemname, bshow);

}
})
},

/**
* create some special type here
*/
handleItemXtype : function(item, form) {
// shortcut
var cc = eb.Const;
var ebtype = eb.Const.ebType;

if (eb.Util.isUndef(item.ebtype)) {
Ext.apply(item, {
xtype : 'textfield'
// anchor : '70%'
});
} else if (item.ebtype === ebtype.yesOrNoComb) {
// a yes or no option combox
Ext.apply(item, {
xtype : 'combo',
hiddenName : item.name,
store : this.yesOrNoStore
});
} else if (item.ebtype === ebtype.abilityComb) {
// a enable or disable combox
Ext.apply(item, {
xtype : 'combo',
hiddenName : item.name,
store : this.abilityStore
});
} else if (item.ebtype === ebtype.stateComb) {
// a state select combox
Ext.apply(item, {
xtype : 'combo',
hiddenName : item.name,
store : this.stateStore
});
} else if (item.ebtype === ebtype.urlComb) {
// a state select combox
Ext.apply(item, {
xtype : 'combo',
hiddenName : item.name,
store : new Ext.data.JsonStore({
url : item.ebUrl,
fields : ['display']
// autoLoad : true
})
});

// sure combo loaded before invoke init combo box value
// item.store.on('load', function() {
// eb.log.debug("combo store loaded");
// var f = this.getForm().findField(item.name);
// f.comboReady = true;
// this.initCombo(f);
// }, this);
}
// else if (item.ebtype === ebtype.multiselect) {
// eb.log.debug('a multiselect');
// Ext.apply(item, {
// xtype : 'textfield'
// listeners : {
// change : function(me, newValue, oldValue) {
// eb.log.debug("changed:" + newValue + "/" + oldValue);
// },
//
// blur : function() {
// eb.log.debug("blur");
// this.handleInheritChange();
// },
// scope : this
// }
// }
// );

// var store = new Ext.data.JsonStore({
// url : item.ebUrl,
// fields : ['display', 'value'],
// autoLoad : true
// });
//
// var ds_empty = new Ext.data.SimpleStore({
// fields : []
// });
//
// Ext.apply(item, {
// xtype : 'itemselector',
//
// // dataFields : ['code', 'desc'],
// // fromData : [['1', 'One'], ['2', 'Two'], ['3', 'Three'],
// // ['4', 'Four'], ['5', 'Five']],
// // toData : [['6', 'Six']],
// // valueField : 'code',
// // displayField : 'desc',
//
// msWidth : 125,
// msHeight : 220,
//
// toLegend : "Selected",
// fromLegend : "Available",
// imagePath : "/ebnms/images/MutiSelect",
//
// valueField : 'value',
// displayField : 'display',
// fromStore : store,
// toStore : ds_empty
// });
//
// // sure list loaded before invoke init multiselect box value
// item.fromStore.on('load', function() {
// eb.log.debug("from store loaded");
// var f = this.getForm().findField(item.name);
// f.fromReady = true;
// this.initMultiSelect(f);
// }, this);
// }

// set some common attribute to combox
if (item.xtype == "combo") {
Ext.apply(item, {
displayField : 'display',
valueField : 'value',
editable : false,
selectOnFocus : true,
lazyInit : false,
mode : 'local',
triggerAction : 'all',
value : cc.combNullValue,
emptyText : cc.combEmptyText
});

// add remote combo property if not url
if (!eb.Util.isUndef(item.ebtype)
&& item.ebtype == cc.ebType.urlComb) {
Ext.apply(item, {
lazyInit : true,
displayField : 'display',
valueField : 'display',
mode : 'remote'
});
}
}
}
});

/**
* a static function for get id of a config type
*
* @param {}
* config
*/
eb.config.BaseConfForm.getNodeId = function(oper, nodeAttr) {
var idstr = "config_" + oper + "_";
for (key in nodeAttr) {
idstr += nodeAttr[key];
idstr += "_";
}

return idstr;
};

// Ext.reg('form', eb.config.BaseConfForm);

alwayscy
14 Sep 2009, 5:31 AM
My code is 1600+ lines long, it seems can not be posted here. But main structure here, use config.panels array construct a very big config array, then pass to FormPanel to create form.

I use sIEve most orphans are fields created in form.



eb.config.BaseConfForm = function(config) {
// create title
var title;
var submitUrl;
switch (config.crudMode) {
case eb.Const.operType.create :
title = eb.Const.menuName.create;
submitUrl = eb.Const.serverUrl.createObject;
break;

case eb.Const.operType.read :
title = eb.Const.menuName.read;
submitUrl = eb.Const.serverUrl.readObject;
break;

case eb.Const.operType.update :
title = eb.Const.menuName.update;
submitUrl = eb.Const.serverUrl.updateObject;
break;

case eb.Const.operType.del :
title = eb.Const.menuName.del;
submitUrl = eb.Const.serverUrl.deleteObject;
break;
}

title += "-" + config.objName;

this.objName = config.objName;

// create tab panel
// for set id of components
var objIdentify = config.nodeAttr.obj_type + '_' + config.nodeAttr.obj_id;
eb.log.debug(title + "for:" + objIdentify);
var panels = [];
for (var i = 0; i < config.panels.length; i++) {
panels.push(this.addPanelTab(objIdentify, config.panels[i]));
}

this.preConfigPanels = config.panels; // store for later use

// apply to Form inside FormPanel
Ext.apply(config, {
id : eb.config.BaseConfForm.getNodeId(config.crudMode, config.nodeAttr),
title : title,
errorReader : new Ext.form.XmlErrorReader(),

reader : new eb.config.NodeValueReader({
record : 'object',// config.objName,
success : '@success'
}, this.myReader),

// autoScroll : true,
// autoHeight : true,

items : {
xtype : 'tabpanel',
enableTabScroll : true,
// layoutOnTabChange : true,
border : false,
// autoScroll : true,
// autoHeight : true,
defaults : {
// autoHeight : true,
// bodyStyle : 'padding:10px'

autoScroll : true
// as we use deferredRender:false we mustn't
// render tabs into display:none containers
// hideMode : 'offsets'
},

// buttonAlign : 'left',
//
// buttons : [{
// text : 'load'
// },{
// text : 'save'
// }],

// for load everything when initing
deferredRender : true,
activeTab : 0,
items : panels,
fs : this,
listeners : {
tabchange : function(panel) {
this.fs.handleInheritChange.call(this.fs);

// because tab pannel render later, need translate value on first active
this.fs.hideValue2Display.call(this.fs, this.fs.getForm());
}
// ,
// bodyresize : function(panel, w, h) {
// eb.log.debug("form tab resize w:" + panel.getInnerWidth()
// + ", h:" + panel.getInnerHeight());
// }
}
}
},

Ext.extend(eb.config.BaseConfForm, Ext.FormPanel, {

Condor
14 Sep 2009, 6:26 AM
Wow, that's a lot of code to work through...

You should check if you are also destroying everything you create (setting stores to autoDestroy:true, destroying dropzone etc.).

alwayscy
14 Sep 2009, 4:54 PM
Thanks! I miss leak problem from beginning, now a lot of work to do!
I'll cut off code first, then, add them back step by step to check.