ExtJs_Ext.form.CompositeField讲解

ExtJs_Ext.form.CompositeField讲解

标签: extjsfunctionstringeachlayoutvalidation
  3138人阅读  评论(0)  收藏  举报
  分类:
/*!
 * Ext JS Library 3.3.0
 */
/**
 * @class Ext.form.CompositeField
 * @extends Ext.form.Field
 * Composite field allowing a number of form Fields to be rendered on the same row. The fields are rendered
 * using an hbox(水平horizontal布局) layout internally(内部地), so all of the normal HBox layout config items are available. 
 * Example usage:
{
   xtype: 'compositefield',
   labelWidth: 120
   items: [
       {
           xtype     : 'textfield',
           fieldLabel: 'Title',
           width     : 20
       },
       {
           xtype     : 'textfield',
           fieldLabel: 'First',
           flex      : 1 //说明:一行中,除了分给Title的20px外,剩余的被分成两等份,First和Last个占一份;
       },
       {
           xtype     : 'textfield',
           fieldLabel: 'Last',
           flex      : 1
       }
   ]
}
 * 
 * In the example above the composite's fieldLabel will be set to 'Title, First, Last' as it groups the fieldLabels
 * of each of its children. This can be overridden by setting a fieldLabel on the compositefield itself:
{
   xtype: 'compositefield',
   fieldLabel: 'Custom label',
   items: [...]
}
 * 
 * Any Ext.form.* component can be placed inside a composite field(所有的表单元素,都可以放进composite组件中);
 */
Ext.form.CompositeField = Ext.extend(Ext.form.Field, {
//属性:
/**
     * @property defaultMargins
     * @type String
     * The margins to apply by default to each field in the composite;
     * 默认子组件之间分隔5像素;
     */
    defaultMargins: '0 5 0 0',
    
/**
     * @property skipLastItemMargin
     * @type Boolean
     * If true, the defaultMargins are not applied to the last item in the composite field set (defaults to true);
     * 当此属性为真时,将略过最后子组件,也就是说,defaultMargins属性不会影响最后子组件;
     */
    skipLastItemMargin: true,
    
/**
     * @property isComposite
     * @type Boolean
     * Signifies(意味着) that this is a Composite field
     */
    isComposite: true,
    
/**
     * @property combineErrors
     * @type Boolean
     * True to combine errors from the individual(单独的) fields into a single error message at the CompositeField
     * level (defaults to true)
     */
    combineErrors: true,
    
/**
     * @cfg {String} labelConnector The string to use when joining segments(部分) of the built label 
     * together (defaults to ', ');(如果子组件配置有各自label属性)合并各个子组件的label,以“,”符号隔开;
     */
    labelConnector: ', ',
    
/**
     * @cfg {Object} defaults Any default properties to assign to the child fields.
     */
//初始化:
   
    initComponent: function() {
        var labels = [],
            items  = this.items || [],
            item;


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


            labels.push(item.fieldLabel);


            //apply any defaults
            Ext.applyIf(item, this.defaults);


            //apply default margins to each item except the last
            if (!(i == j - 1 && this.skipLastItemMargin)) {
                Ext.applyIf(item, {margins: this.defaultMargins});
            }
        }


        //composite fieldLabel:
        this.fieldLabel = this.fieldLabel || this.buildLabel(labels);
        
/**
         * @property fieldErrors
         * @type Ext.util.MixedCollection
         * MixedCollection of current errors on the Composite's subfields. This is used internally to track when
         * to show and hide error messages at the Composite level. Listeners are attached to the MixedCollection's
         * add, remove and replace events to update the error icon in the UI as errors are added or removed.
         */
        this.fieldErrors = new Ext.util.MixedCollection(true, function(item) {
            return item.field;
        });


        this.fieldErrors.on({
            scope  : this,
            add    : this.updateInvalidMark,
            remove : this.updateInvalidMark,
            replace: this.updateInvalidMark
        });


        Ext.form.CompositeField.superclass.initComponent.apply(this, arguments);
        
        this.innerCt = new Ext.Container({
            layout  : 'hbox',
            items   : this.items,
            cls     : 'x-form-composite',
            defaultMargins: '0 3 0 0'
        });
        
        var fields = this.innerCt.findBy(function(c) {
            return c.isFormField;
        }, this);
        
/**
         * @property items
         * @type Ext.util.MixedCollection
         * Internal collection of all of the subfields in this Composite
         */
        this.items = new Ext.util.MixedCollection();
        this.items.addAll(fields);
    },


    /**
     * @private
     * Creates an internal container using hbox and renders the fields to it
     */
    onRender: function(ct, position) {
        if (!this.el) {
/**
             * @property innerCt
             * @type Ext.Container
             * A container configured with hbox layout which is responsible(负责) for laying out the subfields
             */
            var innerCt = this.innerCt;
            innerCt.render(ct);


            this.el = innerCt.getEl();


            //if we're combining subfield errors into a single message, override the markInvalid and clearInvalid
            //methods of each subfield and show them at the Composite level instead
            if (this.combineErrors) {
                this.eachItem(function(field) {
                    Ext.apply(field, {
                        markInvalid : this.onFieldMarkInvalid.createDelegate(this, [field], 0),
                        clearInvalid: this.onFieldClearInvalid.createDelegate(this, [field], 0)
                    });
                });
            }


            //set the label 'for' to the first item
            var l = this.el.parent().parent().child('label', true);
            if (l) {
                l.setAttribute('for', this.items.items[0].id);
            }
        }


        Ext.form.CompositeField.superclass.onRender.apply(this, arguments);
    },


    
/**
     * Called if combineErrors is true and a subfield's markInvalid method is called.
     * By default this just adds the subfield's error to the internal fieldErrors MixedCollection
     * @param {Ext.form.Field} field The field that was marked invalid
     * @param {String} message The error message
     */
    onFieldMarkInvalid: function(field, message) {
        var name  = field.getName(),
            error = {
                field: name, 
                errorName: field.fieldLabel || name,
                error: message
            };


        this.fieldErrors.replace(name, error);


        field.el.addClass(field.invalidClass);
    },


    
/**
     * Called if combineErrors is true and a subfield's clearInvalid method is called.
     * By default this just updates the internal fieldErrors MixedCollection.
     * @param {Ext.form.Field} field The field that was marked invalid
     */
    onFieldClearInvalid: function(field) {
        this.fieldErrors.removeKey(field.getName());


        field.el.removeClass(field.invalidClass);
    },


    /**
     * @private
     * Called after a subfield is marked valid or invalid, this checks to see if any of the subfields are
     * currently invalid. If any subfields are invalid it builds a combined error message marks the composite
     * invalid, otherwise clearInvalid is called
     */
    updateInvalidMark: function() {
        var ieStrict = Ext.isIE6 && Ext.isStrict;


        if (this.fieldErrors.length == 0) {
            this.clearInvalid();


            //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
            if (ieStrict) {
                this.clearInvalid.defer(50, this);
            }
        } else {
            var message = this.buildCombinedErrorMessage(this.fieldErrors.items);


            this.sortErrors();
            this.markInvalid(message);


            //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
            if (ieStrict) {
                this.markInvalid(message);
            }
        }
    },


    
/**
     * Performs(执行) validation checks on each subfield and returns false if any of them fail validation.
     * @return {Boolean} False if any subfield failed validation
     */
    validateValue: function() {
        var valid = true;


        this.eachItem(function(field) {
            if (!field.isValid()) valid = false;
        });


        return valid;
    },


    
/**
     * Takes an object containing error messages for contained fields, returning a combined error
     * string (defaults to just placing each item on a new line). This can be overridden to provide
     * custom combined error message handling.
     * @param {Array} errors Array of errors in format: [{field: 'title', error: 'some error'}]
     * @return {String} The combined error message
     */
    buildCombinedErrorMessage: function(errors) {
        var combined = [],
            error;


        for (var i = 0, j = errors.length; i < j; i++) {
            error = errors[i];


            combined.push(String.format("{0}: {1}", error.errorName, error.error));
        }


        return combined.join("");
    },


    
/**
     * Sorts the internal fieldErrors MixedCollection by the order in which the fields are defined.
     * This is called before displaying errors to ensure that the errors are presented in the expected order.
     * This function can be overridden to provide a custom sorting order if needed.
     */
    sortErrors: function() {
        var fields = this.items;


        this.fieldErrors.sort("ASC", function(a, b) {
            var findByName = function(key) {
                return function(field) {
                    return field.getName() == key;
                };
            };


            var aIndex = fields.findIndexBy(findByName(a.field)),
                bIndex = fields.findIndexBy(findByName(b.field));


            return aIndex < bIndex ? -1 : 1;
        });
    },


    
/**
     * Resets each field in the composite to their previous value
     */
    reset: function() {
        this.eachItem(function(item) {
            item.reset();
        });


        // Defer(延迟) the clearInvalid so if BaseForm's collection is being iterated(重复) 
        // it will be called AFTER it is complete.
        // Important because reset is being called on both the group and the individual(单独的) items.
        (function() {
            this.clearInvalid();
        }).defer(50, this);
    },
    
    
/**
     * Calls clearInvalid on all child fields. This is a convenience(有用的) function and should not often need to be called
     * as fields usually take care of clearing themselves
     */
    clearInvalidChildren: function() {
        this.eachItem(function(item) {
            item.clearInvalid();
        });
    },


    
/**
     * Builds a label string from an array of subfield labels.
     * By default this just joins the labels together with a comma(逗号)
     * @param {Array} segments Array of each of the labels in the composite field's subfields
     * @return {String} The built label
     */
    buildLabel: function(segments) {
     //Ext.clean()_为传递进来的数组创建一个副本,该副本剔除了所有错误的值;
        return Ext.clean(segments).join(this.labelConnector);
    },


    
/**
     * Checks each field in the composite and returns true if any is dirty
     * @return {Boolean} True if any field is dirty
     */
    isDirty: function(){
        //override the behaviour to check sub items.
        if (this.disabled || !this.rendered) {
            return false;
        }


        var dirty = false;
        this.eachItem(function(item){
            if(item.isDirty()){
                dirty = true;
                return false;
            }
        });
        return dirty;
    },


    /**
     * @private
     * Convenience function which passes the given function to every item in the composite
     * @param {Function} fn The function to call
     * @param {Object} scope Optional scope object
     */
    eachItem: function(fn, scope) {
        if(this.items && this.items.each){
            this.items.each(fn, scope || this);
        }
    },


    /**
     * @private
     * Passes the resize call through to the inner panel
     */
    onResize: function(adjWidth, adjHeight, rawWidth, rawHeight) {
        var innerCt = this.innerCt;


        if (this.rendered && innerCt.rendered) {
            innerCt.setSize(adjWidth, adjHeight);
        }


        Ext.form.CompositeField.superclass.onResize.apply(this, arguments);
    },


    /**
     * @private
     * Forces the internal container to be laid out again
     */
    doLayout: function(shallow, force) {
        if (this.rendered) {
            var innerCt = this.innerCt;


            innerCt.forceLayout = this.ownerCt.forceLayout;
            innerCt.doLayout(shallow, force);
        }
    },


    /**
     * @private
     */
    beforeDestroy: function(){
        Ext.destroy(this.innerCt);


        Ext.form.CompositeField.superclass.beforeDestroy.call(this);
    },


    //override the behaviour to check sub items.
    setReadOnly : function(readOnly) {
        if (readOnly == undefined) {
            readOnly = true;
        }
        readOnly = !!readOnly;


        if(this.rendered){
            this.eachItem(function(item){
                item.setReadOnly(readOnly);
            });
        }
        this.readOnly = readOnly;
    },


    onShow : function() {
        Ext.form.CompositeField.superclass.onShow.call(this);
        this.doLayout();
    },


    //override the behaviour to check sub items.
    onDisable : function(){
        this.eachItem(function(item){
            item.disable();
        });
    },


    //override the behaviour to check sub items.
    onEnable : function(){
        this.eachItem(function(item){
            item.enable();
        });
    }
});


Ext.reg('compositefield', Ext.form.CompositeField);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值