2 * @class Ext.form.FieldAncestor
4 A mixin for {@link Ext.container.Container} components that are likely to have form fields in their
5 items subtree. Adds the following capabilities:
7 - Methods for handling the addition and removal of {@link Ext.form.Labelable} and {@link Ext.form.field.Field}
8 instances at any depth within the container.
9 - Events ({@link #fieldvaliditychange} and {@link #fielderrorchange}) for handling changes to the state
10 of individual fields at the container level.
11 - Automatic application of {@link #fieldDefaults} config properties to each field added within the
12 container, to facilitate uniform configuration of all fields.
14 This mixin is primarily for internal use by {@link Ext.form.Panel} and {@link Ext.form.FieldContainer},
15 and should not normally need to be used directly.
18 * @docauthor Jason Johnston <jason@sencha.com>
20 Ext.define('Ext.form.FieldAncestor', {
23 * @cfg {Object} fieldDefaults
24 * <p>If specified, the properties in this object are used as default config values for each
25 * {@link Ext.form.Labelable} instance (e.g. {@link Ext.form.field.Base} or {@link Ext.form.FieldContainer})
26 * that is added as a descendant of this container. Corresponding values specified in an individual field's
27 * own configuration, or from the {@link Ext.container.Container#defaults defaults config} of its parent container,
28 * will take precedence. See the documentation for {@link Ext.form.Labelable} to see what config
29 * options may be specified in the <tt>fieldDefaults</tt>.</p>
31 * <pre><code>new Ext.form.Panel({
56 * <p>In this example, field1 and field2 will get labelAlign:'top' (from the fieldset's <tt>defaults</tt>)
57 * and labelWidth:100 (from <tt>fieldDefaults</tt>), field3 and field4 will both get labelAlign:'left' (from
58 * <tt>fieldDefaults</tt> and field3 will use the labelWidth:150 from its own config.</p>
63 * @protected Initializes the FieldAncestor's state; this must be called from the initComponent method
64 * of any components importing this mixin.
66 initFieldAncestor: function() {
68 onSubtreeChange = me.onFieldAncestorSubtreeChange;
72 * @event fielderrorchange
73 * Fires when the validity state of any one of the {@link Ext.form.field.Field} instances within this
75 * @param {Ext.form.FieldAncestor} this
76 * @param {Ext.form.Labelable} The Field instance whose validity changed
77 * @param {String} isValid The field's new validity state
79 'fieldvaliditychange',
82 * @event fielderrorchange
83 * Fires when the active error message is changed for any one of the {@link Ext.form.Labelable}
84 * instances within this container.
85 * @param {Ext.form.FieldAncestor} this
86 * @param {Ext.form.Labelable} The Labelable instance whose active error was changed
87 * @param {String} error The active error message
92 // Catch addition and removal of descendant fields
93 me.on('add', onSubtreeChange, me);
94 me.on('remove', onSubtreeChange, me);
96 me.initFieldDefaults();
100 * @private Initialize the {@link #fieldDefaults} object
102 initFieldDefaults: function() {
103 if (!this.fieldDefaults) {
104 this.fieldDefaults = {};
110 * Handle the addition and removal of components in the FieldAncestor component's child tree.
112 onFieldAncestorSubtreeChange: function(parent, child) {
114 isAdding = !!child.ownerCt;
116 function handleCmp(cmp) {
117 var isLabelable = cmp.isFieldLabelable,
118 isField = cmp.isFormField;
119 if (isLabelable || isField) {
121 me['onLabelable' + (isAdding ? 'Added' : 'Removed')](cmp);
124 me['onField' + (isAdding ? 'Added' : 'Removed')](cmp);
127 else if (cmp.isContainer) {
128 Ext.Array.forEach(cmp.getRefItems(), handleCmp);
135 * @protected Called when a {@link Ext.form.Labelable} instance is added to the container's subtree.
136 * @param {Ext.form.Labelable} labelable The instance that was added
138 onLabelableAdded: function(labelable) {
141 // buffer slightly to avoid excessive firing while sub-fields are changing en masse
142 me.mon(labelable, 'errorchange', me.handleFieldErrorChange, me, {buffer: 10});
144 labelable.setFieldDefaults(me.fieldDefaults);
148 * @protected Called when a {@link Ext.form.field.Field} instance is added to the container's subtree.
149 * @param {Ext.form.field.Field} field The field which was added
151 onFieldAdded: function(field) {
153 me.mon(field, 'validitychange', me.handleFieldValidityChange, me);
157 * @protected Called when a {@link Ext.form.Labelable} instance is removed from the container's subtree.
158 * @param {Ext.form.Labelable} labelable The instance that was removed
160 onLabelableRemoved: function(labelable) {
162 me.mun(labelable, 'errorchange', me.handleFieldErrorChange, me);
166 * @protected Called when a {@link Ext.form.field.Field} instance is removed from the container's subtree.
167 * @param {Ext.form.field.Field} field The field which was removed
169 onFieldRemoved: function(field) {
171 me.mun(field, 'validitychange', me.handleFieldValidityChange, me);
175 * @private Handle validitychange events on sub-fields; invoke the aggregated event and method
177 handleFieldValidityChange: function(field, isValid) {
179 me.fireEvent('fieldvaliditychange', me, field, isValid);
180 me.onFieldValidityChange();
184 * @private Handle errorchange events on sub-fields; invoke the aggregated event and method
186 handleFieldErrorChange: function(labelable, activeError) {
188 me.fireEvent('fielderrorchange', me, labelable, activeError);
189 me.onFieldErrorChange();
193 * @protected Fired when the validity of any field within the container changes.
194 * @param {Ext.form.field.Field} The sub-field whose validity changed
195 * @param {String} The new validity state
197 onFieldValidityChange: Ext.emptyFn,
200 * @protected Fired when the error message of any field within the container changes.
201 * @param {Ext.form.Labelable} The sub-field whose active error changed
202 * @param {String} The new active error message
204 onFieldErrorChange: Ext.emptyFn