ExtJs疑难杂症之formPanel动态添加FieldSet导致验证失效

[size=large][/size]
这个标题非常拗口,但应该能帮助遇到同样问题的朋友baidu到这里。baidu+google了一下午都没找到答案,发这文章时国内似乎还没有人碰到这个问题,至少没有发相关的帖子出来。但国外被这螃蟹噎到的ExtJs用户已经数不胜数了,还是google见多识广啊~


症状:

当使用add方法在formPanel中动态添加FieldSet时,尽管控件重绘正常,但form.isValid()只会返回true,打印form.items.length时也显示没有新的组件添加进来,但实际上无论render还是submit都是正常的,唯独validate不正常。问题代码如下:

JavaScript代码
//Create the Form
testForm = new Ext.form.FormPanel({
name: "form1",
frame:true,
width: 350,
items: [{
xtype: "textfield",
name: "nom",
fieldLabel: "nom",
allowBlank: false
}]
});
//Render the form
testForm.render(Ext.get('idForm'));
//Create the fieldset
testFieldSet = new Ext.form.FieldSet({
name: 'testFieldSet',
autoHeight: true,
items: [{
xtype : "textfield",
name : "testtextvalid",
fieldLabel: "testDyn",
allowBlank: false
}]
});
testForm.add(testFieldSet); //add the fieldset to the formpanel

testFieldSet.doLayout();
testForm.doLayout();
关于这个bug的解析和解决办法如下(原文地址:http://extjs.com/forum/showthread.php?t=41093):

Containers need to be aware of what they contain.
Most only need to know their immediate children, and so their add method is sufficient.

However, for example a FormPanel, needs to know when Fields are added and removed at any depth in its Component tree so that it can update the Field Collection in its BasicForm.

There have been bug reports that after dynamic adding of Containers to a FormPanel, or of Fields to nested Containers, the BasicForm does not know about those new fields to load.

So I propose two new template methods starting at the Component class level.

onAdd and onRemove

"Template methods" might not be the best name because they don't bubble up the inheritance chain, they bubble up the ownerCt chain.

so Container would have the following new code in red:

add : function(comp){
if(!this.items){
this.initItems();
}
var a = arguments, len = a.length;
if(len > 1){
for(var i = 0; i < len; i++) {
this.add(a[i]);
}
return;
}
var c = this.lookupComponent(this.applyDefaults(comp));
var pos = this.items.length;
if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){
this.items.add(c);
c.ownerCt = this;
this.onAdd(c);
this.fireEvent('add', this, c, pos);
}
return c;
},

insert : function(index, comp){
if(!this.items){
this.initItems();
}
var a = arguments, len = a.length;
if(len > 2){
for(var i = len-1; i >= 1; --i) {
this.insert(index, a[i]);
}
return;
}
var c = this.lookupComponent(this.applyDefaults(comp));

if(c.ownerCt == this && this.items.indexOf(c) < index){
--index;
}

if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
this.items.insert(index, c);
c.ownerCt = this;
this.onAdd(c);
this.fireEvent('add', this, c, index);
}
return c;
},

remove : function(comp, autoDestroy){
var c = this.getComponent(comp);
if(c && this.fireEvent('beforeremove', this, c) !== false){
this.items.remove(c);
delete c.ownerCt;
if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
c.destroy();
}
if(this.layout && this.layout.activeItem == c){
delete this.layout.activeItem;
}
this.onRemove(c);
this.fireEvent('remove', this, c);
}
return c;
},

// private
// Inform all ancestor Containers of an addition to their Component tree
onAdd: function() {
if (this.ownerCt && this.ownerCt.onAdd) {
this.ownerCt.onAdd.apply(this.ownerCt, arguments)
}
},

// private
// Inform all ancestor Containers of a removal from their Component tree
onRemove: function() {
if (this.ownerCt && this.ownerCt.onRemove) {
this.ownerCt.onRemove.apply(this.ownerCt, arguments)
}
}
Then FormPanel would have


// Determine if a Component is usable as a form Field.
isField: function(c) {
return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
},

onAdd : function(c) {
Ext.form.FormPanel.superclass.onAdd.apply(this, arguments);
// If a single form Field, add it
if (this.isField(c)) {
this.form.add(c);

// If a Container, add any Fields it might contain
} else if (c.findBy) {
this.form.add.apply(this.form, c.findBy(this.isField));
}
},

onRemove : function(c) {
Ext.form.FormPanel.superclass.onRemove.apply(this, arguments);
// If a single form Field, remove it
if (this.isField(c)) {
Ext.destroy(c.container.up('.x-form-item'));
this.form.remove(c);

// If a Container, remove any Fields it might contain
} else if (c.findByType) {
Ext.each(c.findBy(this.isField), this.form.remove, this.form);
}
},
Untested code, but that's the principle.

You see that it uses find by type which uses isXType which, as discusses on another thread gives up testing the inheritance chain for an xtype match as soon as it hits a constructor with no xtype...


上面是根本性的解决办法。

但对于大部分用户,我还是建议用以下workaround代替。

解决办法:

XML/HTML代码
//Create the Form
testForm = new Ext.form.FormPanel({
name: "form1",
frame:true,
width: 350,
items: [{
xtype: "textfield",
name: "nom",
fieldLabel: "nom",
allowBlank: false
}]
});
//Render the form
testForm.render(Ext.get('idForm'));
//Create the fieldset
testFieldSet = new Ext.form.FieldSet({
name: 'testFieldSet',
autoHeight: true,
items: [{
xtype : "textfield",
name : "testtextvalid",
fieldLabel: "testDyn",
allowBlank: false
}]
});
testForm.add(testFieldSet); //add the fieldset to the formpanel

testForm.doLayout();
===========this is the workaround============
testFieldSet.form = testForm.getForm();
testForm.initFields.call(testFieldSet);
//delete testForm.form;原来是有这一行的,但加上后连getForm()都不好用了,去掉就没问题了
===========this is the workaround============
OK,一切恢复正常!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值