Page 1 of 2 12 LastLast
Results 1 to 10 of 98

Thread: [2.0.1/2.1] Field.destroy() on Fields rendered by FormLayout does not clean up.

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,890

    Default [2.0.1/2.1] Field.destroy() on Fields rendered by FormLayout does not clean up.

    When an Ext.form.Field is rendered by a FormLayout, it adds a label, and wraps it in a <div class='x-form-item'>

    If you then call myFormPanel.remove(myField, true), or myField.destroy(), the Field itself only knows how to remove its own DOM structure, not that added by the FormLayout.

    The FormLayout needs to add handlers to remove its extra elements.

    This becomes important if you have a very dynamic UI and are adding and removing fields as I have to in a specific use case like dynamically building a filter for a server query:



    The following override fixes it:

    Code:
    Ext.override(Ext.layout.FormLayout, {
        renderItem : function(c, position, target){
            if(c && !c.rendered && c.isFormField && c.inputType != 'hidden'){
                var args = [
                       c.id, c.fieldLabel,
                       c.labelStyle||this.labelStyle||'',
                       this.elementStyle||'',
                       typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
                       (c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
                       c.clearCls || 'x-form-clear-left' 
                ];
                if(typeof position == 'number'){
                    position = target.dom.childNodes[position] || null;
                }
                if(position){
                    c.formItem = this.fieldTpl.insertBefore(position, args, true);
                }else{
                    c.formItem = this.fieldTpl.append(target, args, true);
                }
    
    //          Remove the form layout wrapper on Field destroy.
                c.on('destroy', c.formItem.remove, c.formItem, {single: true});
                c.render('x-form-el-'+c.id);
            }else {
                Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
            }
        }
    });

  2. #2
    Sencha User Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,245

    Default

    I prefer this override (Ext 2.x):
    Code:
    Ext.override(Ext.layout.FormLayout, {
    	renderItem : function(c, position, target){
    		if(c && !c.rendered && c.isFormField && c.inputType != 'hidden'){
    			var args = [
    				   c.id, c.fieldLabel,
    				   c.labelStyle||this.labelStyle||'',
    				   this.elementStyle||'',
    				   typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
    				   (c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
    				   c.clearCls || 'x-form-clear-left' 
    			];
    			if(typeof position == 'number'){
    				position = target.dom.childNodes[position] || null;
    			}
    			if(position){
    				c.itemCt = this.fieldTpl.insertBefore(position, args, true);
    			}else{
    				c.itemCt = this.fieldTpl.append(target, args, true);
    			}
    			c.actionMode = 'itemCt';
    			c.render('x-form-el-'+c.id);
    			c.container = c.itemCt;
    			c.actionMode = 'container';
    		}else {
    			Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
    		}
    	},
    });
    Ext.override(Ext.form.TriggerField, {
    	actionMode: 'wrap',
    	onShow: Ext.form.TriggerField.superclass.onShow,
    	onHide: Ext.form.TriggerField.superclass.onHide
    });
    Ext.override(Ext.form.Checkbox, {
    	actionMode: 'wrap',
    	getActionEl: Ext.form.Checkbox.superclass.getActionEl
    });
    Ext.override(Ext.form.HtmlEditor, {
    	actionMode: 'wrap'
    });
    This not only makes destroy(), but also show(), hide(), disable() and enable() act on the label too.

    For Ext 3.0.0 you need to change the override slightly:
    Code:
    Ext.override(Ext.layout.FormLayout, {
    	renderItem : function(c, position, target){
    		if(c && !c.rendered && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
    			var args = this.getTemplateArgs(c);
    			if(typeof position == 'number'){
    				position = target.dom.childNodes[position] || null;
    			}
    			if(position){
    				c.itemCt = this.fieldTpl.insertBefore(position, args, true);
    			}else{
    				c.itemCt = this.fieldTpl.append(target, args, true);
    			}
    			c.actionMode = 'itemCt';
    			c.render('x-form-el-'+c.id);
    			c.container = c.itemCt;
    			c.actionMode = 'container';
    		}else {
    			Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
    		}
    	}
    });
    Ext.override(Ext.form.Field, {
    	getItemCt : function(){
    		return this.itemCt;
    	}
    });
    For Ext 3.0.1 and up you only need:
    Code:
    Ext.layout.FormLayout.prototype.trackLabels = true;
    Last edited by Condor; 26 Aug 2009 at 10:13 PM. Reason: Fixed in Ext 3.0.1

  3. #3
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,890

    Default

    I checked that. The container element is not the .x-form-item, it is the .x-form-element which wraps the input field.

    The .x-form-item, whcih contains the label and the "clear" div is still there when the container is removed.

  4. #4
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,890

    Default

    Setting the container to the .x-form-item and then actionMode = 'container' makes destroy work.

    I don't know what the other implications are of having the container that "far out" on the actionMode, but this fixes my cleanup problem:

    Code:
    Ext.override(Ext.layout.FormLayout, {
        renderItem : function(c, position, target){
            if(c && !c.rendered && c.isFormField && c.inputType != 'hidden'){
                var args = [
                       c.id, c.fieldLabel,
                       c.labelStyle||this.labelStyle||'',
                       this.elementStyle||'',
                       typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
                       (c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
                       c.clearCls || 'x-form-clear-left' 
                ];
                if(typeof position == 'number'){
                    position = target.dom.childNodes[position] || null;
                }
                if(position){
                    c.formItem = this.fieldTpl.insertBefore(position, args, true);
                }else{
                    c.formItem = this.fieldTpl.append(target, args, true);
                }
                c.actionMode = 'formItem';
                c.render('x-form-el-'+c.id);
                c.container = c.formItem;
                c.actionMode = 'container';
            }else {
                Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
            }
        }
    });
    
    Ext.override(Ext.form.TriggerField, {
    	actionMode: 'wrap',
    	onShow: Ext.form.TriggerField.superclass.onShow,
    	onHide: Ext.form.TriggerField.superclass.onHide
    });

  5. #5
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,890

    Default

    That makes hide and show work fully if you also set hideParent to true.

    Perhaps FormLayout should set hidParent to true so that hide/show acts on the full Field, label included? I know people have posted a bug report that Field.hide does not hide the Field's label.

    What does the Ext core team think?

  6. #6
    Sencha User Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,245

    Default

    Setting actionMode:'container' already makes ExtJS show and hide the container (getActionEl() == container), so there is no need to set hideParent:true.

    Where you referring to this post?

    ps. Aren't you missing a Ext.get() (see other post) ?

  7. #7
    igor.stama
    Guest

    Default

    Greetings Animal and Condor,
    thank you for the override code.

    Let me first say how wonderful the ExtJS framework is, but as always there are some quirks like this.

    I have a formPanel and it will contain, radios, textfields, comboboxes etc that need to be hidden or need to affect the visibility of other fields on the form. As usual the labels are not hidden but we have added your fix to extend the Ext.FormPanel code at the moment it doesn't work for a combo box field. Any help appriciated \0/

    Here is the use-case and my investigation using Firebug:

    When a combo is present on a form it does not add x-hide-display prior to the x-form-item class, but instead it adds display: none; to the style of the combo and as a result it does not hide the label .

    I would be available to give you additional information if you need them.

    I'm using version 2.1 of ExtJS.

  8. #8
    Sencha User Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,245

    Default

    Quote Originally Posted by igor.stama View Post
    ...
    we have added your fix to extend the Ext.FormPanel code at the moment it doesn't work for a combo box field
    ...
    Combobox completely ignores the normal field show and hide and uses its own logic.
    In your case you could replace the onShow and onHide methods with the original code, e.g.

    Code:
    {
    	xtype: 'combo',
    	onShow: Ext.form.TextField.prototype.onShow,
    	onHide: Ext.form.TextField.prototype.onHide,
    	...
    }
    or as an override:

    Code:
    Ext.override(Ext.form.TriggerField, {
    	actionMode: 'wrap',
    	onShow: Ext.form.TriggerField.superclass.onShow,
    	onHide: Ext.form.TriggerField.superclass.onHide
    });
    Last edited by Condor; 30 Dec 2008 at 4:35 AM. Reason: Added override

  9. #9
    Sencha User
    Join Date
    Feb 2009
    Posts
    38

    Default

    Great, this is what I need.
    "Despite the tons of examples and docs, EXT JS is voodoo. Damned cool voodoo, but still voodoo.''

    (c) Brian Moore (retell by CoL)

  10. #10
    Ext User
    Join Date
    Aug 2009
    Posts
    100

    Default

    Great news on the 3.0.1 fix, but when will this release be available to the public?

    Thanks,
    Matt

    Quote Originally Posted by Condor View Post
    I prefer this override (Ext 2.x):
    Code:
    Ext.override(Ext.layout.FormLayout, {
        renderItem : function(c, position, target){
            if(c && !c.rendered && c.isFormField && c.inputType != 'hidden'){
                var args = [
                       c.id, c.fieldLabel,
                       c.labelStyle||this.labelStyle||'',
                       this.elementStyle||'',
                       typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
                       (c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
                       c.clearCls || 'x-form-clear-left' 
                ];
                if(typeof position == 'number'){
                    position = target.dom.childNodes[position] || null;
                }
                if(position){
                    c.itemCt = this.fieldTpl.insertBefore(position, args, true);
                }else{
                    c.itemCt = this.fieldTpl.append(target, args, true);
                }
                c.actionMode = 'itemCt';
                c.render('x-form-el-'+c.id);
                c.container = c.itemCt;
                c.actionMode = 'container';
            }else {
                Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
            }
        },
    });
    Ext.override(Ext.form.TriggerField, {
        actionMode: 'wrap',
        onShow: Ext.form.TriggerField.superclass.onShow,
        onHide: Ext.form.TriggerField.superclass.onHide
    });
    Ext.override(Ext.form.Checkbox, {
        actionMode: 'wrap',
        getActionEl: Ext.form.Checkbox.superclass.getActionEl
    });
    Ext.override(Ext.form.HtmlEditor, {
        actionMode: 'wrap'
    });
    This not only makes destroy(), but also show(), hide(), disable() and enable() act on the label too.

    For Ext 3.0.0 you need to change the override slightly:
    Code:
    Ext.override(Ext.layout.FormLayout, {
        renderItem : function(c, position, target){
            if(c && !c.rendered && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
                var args = this.getTemplateArgs(c);
                if(typeof position == 'number'){
                    position = target.dom.childNodes[position] || null;
                }
                if(position){
                    c.itemCt = this.fieldTpl.insertBefore(position, args, true);
                }else{
                    c.itemCt = this.fieldTpl.append(target, args, true);
                }
                c.actionMode = 'itemCt';
                c.render('x-form-el-'+c.id);
                c.container = c.itemCt;
                c.actionMode = 'container';
            }else {
                Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
            }
        }
    });
    Ext.override(Ext.form.Field, {
        getItemCt : function(){
            return this.itemCt;
        }
    });
    For Ext 3.0.1 and up you only need:
    Code:
    Ext.layout.FormLayout.prototype.trackLabels = true;

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •