X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/form/FieldAncestor.js?ds=sidebyside diff --git a/src/form/FieldAncestor.js b/src/form/FieldAncestor.js new file mode 100644 index 00000000..e8b8e6f5 --- /dev/null +++ b/src/form/FieldAncestor.js @@ -0,0 +1,206 @@ +/** + * @class Ext.form.FieldAncestor + +A mixin for {@link Ext.container.Container} components that are likely to have form fields in their +items subtree. Adds the following capabilities: + +- Methods for handling the addition and removal of {@link Ext.form.Labelable} and {@link Ext.form.field.Field} + instances at any depth within the container. +- Events ({@link #fieldvaliditychange} and {@link #fielderrorchange}) for handling changes to the state + of individual fields at the container level. +- Automatic application of {@link #fieldDefaults} config properties to each field added within the + container, to facilitate uniform configuration of all fields. + +This mixin is primarily for internal use by {@link Ext.form.Panel} and {@link Ext.form.FieldContainer}, +and should not normally need to be used directly. + + * @markdown + * @docauthor Jason Johnston + */ +Ext.define('Ext.form.FieldAncestor', { + + /** + * @cfg {Object} fieldDefaults + *

If specified, the properties in this object are used as default config values for each + * {@link Ext.form.Labelable} instance (e.g. {@link Ext.form.field.Base} or {@link Ext.form.FieldContainer}) + * that is added as a descendant of this container. Corresponding values specified in an individual field's + * own configuration, or from the {@link Ext.container.Container#defaults defaults config} of its parent container, + * will take precedence. See the documentation for {@link Ext.form.Labelable} to see what config + * options may be specified in the fieldDefaults.

+ *

Example:

+ *
new Ext.form.Panel({
+    fieldDefaults: {
+        labelAlign: 'left',
+        labelWidth: 100
+    },
+    items: [{
+        xtype: 'fieldset',
+        defaults: {
+            labelAlign: 'top'
+        },
+        items: [{
+            name: 'field1'
+        }, {
+            name: 'field2'
+        }]
+    }, {
+        xtype: 'fieldset',
+        items: [{
+            name: 'field3',
+            labelWidth: 150
+        }, {
+            name: 'field4'
+        }]
+    }]
+});
+ *

In this example, field1 and field2 will get labelAlign:'top' (from the fieldset's defaults) + * and labelWidth:100 (from fieldDefaults), field3 and field4 will both get labelAlign:'left' (from + * fieldDefaults and field3 will use the labelWidth:150 from its own config.

+ */ + + + /** + * @protected Initializes the FieldAncestor's state; this must be called from the initComponent method + * of any components importing this mixin. + */ + initFieldAncestor: function() { + var me = this, + onSubtreeChange = me.onFieldAncestorSubtreeChange; + + me.addEvents( + /** + * @event fielderrorchange + * Fires when the validity state of any one of the {@link Ext.form.field.Field} instances within this + * container changes. + * @param {Ext.form.FieldAncestor} this + * @param {Ext.form.Labelable} The Field instance whose validity changed + * @param {String} isValid The field's new validity state + */ + 'fieldvaliditychange', + + /** + * @event fielderrorchange + * Fires when the active error message is changed for any one of the {@link Ext.form.Labelable} + * instances within this container. + * @param {Ext.form.FieldAncestor} this + * @param {Ext.form.Labelable} The Labelable instance whose active error was changed + * @param {String} error The active error message + */ + 'fielderrorchange' + ); + + // Catch addition and removal of descendant fields + me.on('add', onSubtreeChange, me); + me.on('remove', onSubtreeChange, me); + + me.initFieldDefaults(); + }, + + /** + * @private Initialize the {@link #fieldDefaults} object + */ + initFieldDefaults: function() { + if (!this.fieldDefaults) { + this.fieldDefaults = {}; + } + }, + + /** + * @private + * Handle the addition and removal of components in the FieldAncestor component's child tree. + */ + onFieldAncestorSubtreeChange: function(parent, child) { + var me = this, + isAdding = !!child.ownerCt; + + function handleCmp(cmp) { + var isLabelable = cmp.isFieldLabelable, + isField = cmp.isFormField; + if (isLabelable || isField) { + if (isLabelable) { + me['onLabelable' + (isAdding ? 'Added' : 'Removed')](cmp); + } + if (isField) { + me['onField' + (isAdding ? 'Added' : 'Removed')](cmp); + } + } + else if (cmp.isContainer) { + Ext.Array.forEach(cmp.getRefItems(), handleCmp); + } + } + handleCmp(child); + }, + + /** + * @protected Called when a {@link Ext.form.Labelable} instance is added to the container's subtree. + * @param {Ext.form.Labelable} labelable The instance that was added + */ + onLabelableAdded: function(labelable) { + var me = this; + + // buffer slightly to avoid excessive firing while sub-fields are changing en masse + me.mon(labelable, 'errorchange', me.handleFieldErrorChange, me, {buffer: 10}); + + labelable.setFieldDefaults(me.fieldDefaults); + }, + + /** + * @protected Called when a {@link Ext.form.field.Field} instance is added to the container's subtree. + * @param {Ext.form.field.Field} field The field which was added + */ + onFieldAdded: function(field) { + var me = this; + me.mon(field, 'validitychange', me.handleFieldValidityChange, me); + }, + + /** + * @protected Called when a {@link Ext.form.Labelable} instance is removed from the container's subtree. + * @param {Ext.form.Labelable} labelable The instance that was removed + */ + onLabelableRemoved: function(labelable) { + var me = this; + me.mun(labelable, 'errorchange', me.handleFieldErrorChange, me); + }, + + /** + * @protected Called when a {@link Ext.form.field.Field} instance is removed from the container's subtree. + * @param {Ext.form.field.Field} field The field which was removed + */ + onFieldRemoved: function(field) { + var me = this; + me.mun(field, 'validitychange', me.handleFieldValidityChange, me); + }, + + /** + * @private Handle validitychange events on sub-fields; invoke the aggregated event and method + */ + handleFieldValidityChange: function(field, isValid) { + var me = this; + me.fireEvent('fieldvaliditychange', me, field, isValid); + me.onFieldValidityChange(); + }, + + /** + * @private Handle errorchange events on sub-fields; invoke the aggregated event and method + */ + handleFieldErrorChange: function(labelable, activeError) { + var me = this; + me.fireEvent('fielderrorchange', me, labelable, activeError); + me.onFieldErrorChange(); + }, + + /** + * @protected Fired when the validity of any field within the container changes. + * @param {Ext.form.field.Field} The sub-field whose validity changed + * @param {String} The new validity state + */ + onFieldValidityChange: Ext.emptyFn, + + /** + * @protected Fired when the error message of any field within the container changes. + * @param {Ext.form.Labelable} The sub-field whose active error changed + * @param {String} The new active error message + */ + onFieldErrorChange: Ext.emptyFn + +}); \ No newline at end of file