2 * A mixin which allows a component to be configured and decorated with a label and/or error message as is
3 * common for form fields. This is used by e.g. Ext.form.field.Base and Ext.form.FieldContainer
4 * to let them be managed by the Field layout.
6 * NOTE: This mixin is mainly for internal library use and most users should not need to use it directly. It
7 * is more likely you will want to use one of the component classes that import this mixin, such as
8 * Ext.form.field.Base or Ext.form.FieldContainer.
10 * Use of this mixin does not make a component a field in the logical sense, meaning it does not provide any
11 * logic or state related to values or validation; that is handled by the related Ext.form.field.Field
12 * mixin. These two mixins may be used separately (for example Ext.form.FieldContainer is Labelable but not a
13 * Field), or in combination (for example Ext.form.field.Base implements both and has logic for connecting the
16 * Component classes which use this mixin should use the Field layout
17 * or a derivation thereof to properly size and position the label and message according to the component config.
18 * They must also call the {@link #initLabelable} method during component initialization to ensure the mixin gets
21 Ext
.define("Ext.form.Labelable", {
26 'Ext.overrides.dom.Element'
35 beforeRender
: 'beforeLabelRender',
36 onRender
: 'onLabelRender'
43 * @property {Ext.dom.Element} labelEl
44 * The label Element for this component. Only available after the component has been rendered.
49 * @property {Ext.dom.Element} bodyEl
50 * The div Element wrapping the component's contents. Only available after the component has been rendered.
55 * @property {Ext.dom.Element} errorEl
56 * The div Element that will contain the component's error message(s). Note that depending on the configured
57 * {@link #msgTarget}, this element may be hidden in favor of some other form of presentation, but will always
58 * be present in the DOM for use by assistive technologies.
68 * @cfg {String/String[]/Ext.XTemplate} labelableRenderTpl
69 * The rendering template for the field decorations. Component classes using this mixin
70 * should include logic to use this as their {@link Ext.Component#renderTpl renderTpl},
71 * and implement the {@link #getSubTplMarkup} method to generate the field body content.
76 '<label id="{id}-labelEl" data-ref="labelEl" class="{labelCls} {labelCls}-{ui} {labelClsExtra} ',
77 '{childElCls} {unselectableCls}" style="{labelStyle}"<tpl if="inputId">',
78 ' for="{inputId}"</tpl> {labelAttrTpl}>',
79 '<span class="{labelInnerCls} {labelInnerCls}-{ui}" style="{labelInnerStyle}">',
80 '{beforeLabelTextTpl}',
81 '<tpl if="fieldLabel">{fieldLabel}',
82 '<tpl if="labelSeparator">{labelSeparator}</tpl>',
84 '{afterLabelTextTpl}',
88 '<div id="{id}-bodyEl" data-ref="bodyEl" role="presentation"',
89 ' class="{baseBodyCls} {baseBodyCls}-{ui}<tpl if="fieldBodyCls">',
90 ' {fieldBodyCls} {fieldBodyCls}-{ui}</tpl> {growCls} {extraFieldBodyCls}"',
91 '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
94 '{[values.$comp.getSubTplMarkup(values)]}',
97 // Unlike errorEl below ariaErrorEl is always rendered but is clipped out of existence
98 '<div id="{id}-ariaErrorEl" data-ref="ariaErrorEl" role="alert" aria-live="polite"',
99 ' class="' + Ext
.baseCSSPrefix
+ 'hidden-clip">',
102 '<tpl if="renderError">',
103 '<div id="{id}-errorWrapEl" data-ref="errorWrapEl" class="{errorWrapCls} {errorWrapCls}-{ui}',
104 ' {errorWrapExtraCls}" style="{errorWrapStyle}">',
105 '<div role="presentation" id="{id}-errorEl" data-ref="errorEl" ',
106 'class="{errorMsgCls} {invalidMsgCls} {invalidMsgCls}-{ui}" ',
107 'data-anchorTarget="{tipAnchorTarget}">',
117 * @cfg {String/String[]/Ext.XTemplate} activeErrorsTpl
118 * The template used to format the Array of error messages passed to {@link #setActiveErrors} into a single HTML
119 * string. if the {@link #msgTarget} is title, it defaults to a list separated by new lines. Otherwise, it
120 * renders each message as an item in an unordered list.
122 activeErrorsTpl
: undefined,
124 htmlActiveErrorsTpl
: [
125 '<tpl if="errors && errors.length">',
126 '<ul class="{listCls}">',
127 '<tpl for="errors"><li>{.}</li></tpl>',
132 plaintextActiveErrorsTpl
: [
133 '<tpl if="errors && errors.length">',
134 '<tpl for="errors"><tpl if="xindex > 1">\n</tpl>{.}</tpl>',
139 * @property {Boolean} isFieldLabelable
140 * Flag denoting that this object is labelable as a field. Always true.
142 isFieldLabelable
: true,
145 * @cfg {String} formItemCls
146 * A CSS class to be applied to the outermost element to denote that it is participating in the form field layout.
148 formItemCls
: Ext
.baseCSSPrefix
+ 'form-item',
151 * @cfg {String} labelCls
152 * The CSS class to be applied to the label element. This (single) CSS class is used to formulate the renderSelector
153 * and drives the field layout where it is concatenated with a hyphen ('-') and {@link #labelAlign}. To add
154 * additional classes, use {@link #labelClsExtra}.
156 labelCls
: Ext
.baseCSSPrefix
+ 'form-item-label',
161 topLabelCls
: Ext
.baseCSSPrefix
+ 'form-item-label-top',
162 rightLabelCls
: Ext
.baseCSSPrefix
+ 'form-item-label-right',
163 labelInnerCls
: Ext
.baseCSSPrefix
+ 'form-item-label-inner',
164 topLabelSideErrorCls
: Ext
.baseCSSPrefix
+ 'form-item-label-top-side-error',
167 * @cfg {String} labelClsExtra
168 * An optional string of one or more additional CSS classes to add to the label element. Defaults to empty.
172 * @cfg {String} errorMsgCls
173 * The CSS class to be applied to the error message element.
175 errorMsgCls
: Ext
.baseCSSPrefix
+ 'form-error-msg',
177 errorWrapCls
: Ext
.baseCSSPrefix
+ 'form-error-wrap',
178 errorWrapSideCls
: Ext
.baseCSSPrefix
+ 'form-error-wrap-side',
179 errorWrapUnderCls
: Ext
.baseCSSPrefix
+ 'form-error-wrap-under',
180 errorWrapUnderSideLabelCls
: Ext
.baseCSSPrefix
+ 'form-error-wrap-under-side-label',
183 * @cfg {String} baseBodyCls
184 * The CSS class to be applied to the body content element.
186 baseBodyCls
: Ext
.baseCSSPrefix
+ 'form-item-body',
188 invalidIconCls
: Ext
.baseCSSPrefix
+ 'form-invalid-icon',
190 invalidUnderCls
: Ext
.baseCSSPrefix
+ 'form-invalid-under',
192 noLabelCls
: Ext
.baseCSSPrefix
+ 'form-item-no-label',
195 * @cfg {String} fieldBodyCls
196 * An extra CSS class to be applied to the body content element in addition to {@link #baseBodyCls}.
201 * @cfg {String} invalidCls
202 * The CSS class to use when marking the component invalid.
204 invalidCls
: Ext
.baseCSSPrefix
+ 'form-invalid',
207 * @cfg {String} fieldLabel
208 * The label for the field. It gets appended with the {@link #labelSeparator}, and its position and sizing is
209 * determined by the {@link #labelAlign} and {@link #labelWidth} configs.
211 fieldLabel
: undefined,
214 * @cfg {String} labelAlign
215 * Controls the position and alignment of the {@link #fieldLabel}. Valid values are:
217 * - "left" (the default) - The label is positioned to the left of the field, with its text aligned to the left.
218 * Its width is determined by the {@link #labelWidth} config.
219 * - "top" - The label is positioned above the field.
220 * - "right" - The label is positioned to the left of the field, with its text aligned to the right.
221 * Its width is determined by the {@link #labelWidth} config.
226 * @cfg {Number} labelWidth
227 * The width of the {@link #fieldLabel} in pixels. Only applicable if {@link #labelAlign}
228 * is set to "left" or "right".
233 * @cfg {Number} labelPad
234 * The amount of space in pixels between the {@link #fieldLabel} and the field body.
235 * This defaults to `5` for compatibility with Ext JS 4, however, as of Ext JS 5
236 * the space between the label and the body can optionally be determined by the theme
237 * using the {@link #$form-label-horizontal-spacing} (for side-aligned labels) and
238 * {@link #$form-label-vertical-spacing} (for top-aligned labels) SASS variables.
239 * In order for the stylesheet values as to take effect, you must use a labelPad value
246 * @cfg {String} labelSeparator
247 * Character(s) to be inserted at the end of the {@link #fieldLabel label text}.
249 * Set to empty string to hide the separator completely.
251 labelSeparator
: ':',
255 * @cfg {String} labelStyle
256 * A CSS style specification string to apply directly to this field's label.
260 * @cfg {Boolean} hideLabel
261 * Set to true to completely hide the label element ({@link #fieldLabel} and {@link #labelSeparator}). Also see
262 * {@link #hideEmptyLabel}, which controls whether space will be reserved for an empty fieldLabel.
267 * @cfg {Boolean} hideEmptyLabel
268 * When set to true, the label element ({@link #fieldLabel} and {@link #labelSeparator}) will be automatically
269 * hidden if the {@link #fieldLabel} is empty. Setting this to false will cause the empty label element to be
270 * rendered and space to be reserved for it; this is useful if you want a field without a label to line up with
271 * other labeled fields in the same form.
273 * If you wish to unconditionall hide the label even if a non-empty fieldLabel is configured, then set the
274 * {@link #hideLabel} config to true.
276 hideEmptyLabel
: true,
279 * @cfg {Boolean} preventMark
280 * true to disable displaying any {@link #setActiveError error message} set on this object.
285 * @cfg {Boolean} autoFitErrors
286 * Whether to adjust the component's body width to make room for 'side'
287 * {@link #msgTarget error messages}.
292 * @cfg {String} msgTarget
293 * The location where the error message text should display. Must be one of the following values:
295 * - `qtip` Display a quick tip containing the message when the user hovers over the field.
296 * This is the default.
298 * **{@link Ext.tip.QuickTipManager#init} must have been called for this setting to work.**
300 * - `title` Display the message in a default browser title attribute popup.
301 * - `under` Add a block div beneath the field containing the error message.
302 * - `side` Add an error icon to the right of the field, displaying the message in a popup on hover.
303 * - `none` Don't display any error message. This might be useful if you are implementing custom error display.
304 * - `[element id]` Add the error message directly to the innerHTML of the specified element.
310 * Map for msg target lookup, if target is not in this map it is assumed
311 * to be an element id
322 * @cfg {String} activeError
323 * If specified, then the component will be displayed with this value as its active error when first rendered. Use
324 * {@link #setActiveError} or {@link #unsetActiveError} to change it after component creation.
329 * Tells the layout system that the height can be measured immediately because the width does not need setting.
333 labelableInsertions
: [
336 * @cfg {String/Array/Ext.XTemplate} beforeBodyEl
337 * An optional string or `XTemplate` configuration to insert in the field markup
338 * at the beginning of the input containing element. If an `XTemplate` is used, the component's {@link Ext.Component#renderData render data}
339 * serves as the context.
344 * @cfg {String/Array/Ext.XTemplate} afterBodyEl
345 * An optional string or `XTemplate` configuration to insert in the field markup
346 * at the end of the input containing element. If an `XTemplate` is used, the component's {@link Ext.Component#renderData render data}
347 * serves as the context.
352 * @cfg {String/Array/Ext.XTemplate} beforeLabelTpl
353 * An optional string or `XTemplate` configuration to insert in the field markup
354 * before the label element. If an `XTemplate` is used, the component's {@link Ext.Component#renderData render data}
355 * serves as the context.
360 * @cfg {String/Array/Ext.XTemplate} afterLabelTpl
361 * An optional string or `XTemplate` configuration to insert in the field markup
362 * after the label element. If an `XTemplate` is used, the component's {@link Ext.Component#renderData render data}
363 * serves as the context.
368 * @cfg {String/Array/Ext.XTemplate} beforeSubTpl
369 * An optional string or `XTemplate` configuration to insert in the field markup
370 * before the {@link #getSubTplMarkup subTpl markup}. If an `XTemplate` is used, the
371 * component's {@link Ext.Component#renderData render data} serves as the context.
376 * @cfg {String/Array/Ext.XTemplate} afterSubTpl
377 * An optional string or `XTemplate` configuration to insert in the field markup
378 * after the {@link #getSubTplMarkup subTpl markup}. If an `XTemplate` is used, the
379 * component's {@link Ext.Component#renderData render data} serves as the context.
384 * @cfg {String/Array/Ext.XTemplate} beforeLabelTextTpl
385 * An optional string or `XTemplate` configuration to insert in the field markup
386 * before the label text. If an `XTemplate` is used, the component's {@link Ext.Component#renderData render data}
387 * serves as the context.
389 'beforeLabelTextTpl',
392 * @cfg {String/Array/Ext.XTemplate} afterLabelTextTpl
393 * An optional string or `XTemplate` configuration to insert in the field markup
394 * after the label text. If an `XTemplate` is used, the component's {@link Ext.Component#renderData render data}
395 * serves as the context.
400 * @cfg {String/Array/Ext.XTemplate} labelAttrTpl
401 * An optional string or `XTemplate` configuration to insert in the field markup
402 * inside the label element (as attributes). If an `XTemplate` is used, the component's
403 * {@link Ext.Component#renderData render data} serves as the context.
410 * Use a custom QuickTip instance separate from the main QuickTips singleton, so that we
411 * can give it a custom frame style. Responds to errorqtip rather than the qtip property.
415 initTip: function() {
424 id
: 'ext-form-error-tip',
426 // tell the spec runner to ignore this element when checking if the dom is clean
432 // On Touch devices, tapping the target shows the qtip
433 if (Ext
.supports
.Touch
) {
434 cfg
.dismissDelay
= 0;
438 beforeshow: function() {
439 this.minWidth
= Ext
.fly(this.anchorTarget
).getWidth();
443 tip
= this.tip
= Ext
.create('Ext.tip.QuickTip', cfg
);
444 copy
= Ext
.apply({}, tip
.tagConfig
);
445 copy
.attribute
= 'errorqtip';
446 tip
.setTagConfig(copy
);
450 * Destroy the error tip instance.
453 destroyTip: function() {
454 this.tip
= Ext
.destroy(this.tip
);
460 * Fires when the active error message is changed via {@link #setActiveError}.
461 * @param {Ext.form.Labelable} this
462 * @param {String} error The active error message
466 * Performs initialization of this mixin. Component classes using this mixin should call this method during their
467 * own initialization.
469 initLabelable: function() {
471 padding
= me
.padding
;
473 // This Component is rendered as a table. Padding doesn't work on tables
474 // Before padding can be applied to the encapsulating table element, copy the padding into
475 // an extraMargins property which is to be added to all computed margins post render :(
477 me
.padding
= undefined;
478 me
.extraMargins
= Ext
.Element
.parseBox(padding
);
481 // IE8 hack for https://sencha.jira.com/browse/EXTJS-17536.
482 // Need to force a relayout of the display:table form item.
483 // TODO: Remove when IE8 retires.
485 me
.restoreDisplay
= Ext
.Function
.createDelayed(me
.doRestoreDisplay
, 0, me
);
488 if (!me
.activeErrorsTpl
) {
489 if (me
.msgTarget
=== 'title') {
490 me
.activeErrorsTpl
= me
.plaintextActiveErrorsTpl
;
492 me
.activeErrorsTpl
= me
.htmlActiveErrorsTpl
;
496 me
.addCls([me
.formItemCls
, me
.formItemCls
+ '-' + me
.ui
]);
498 // Prevent first render of active error, at Field render time from signalling a change from undefined to "
499 me
.lastActiveError
= '';
501 // bubbleEvents on the prototype of a mixin won't work, so call enableBubble
502 me
.enableBubble('errorchange');
506 * Returns the trimmed label by slicing off the label separator character. Can be overridden.
507 * @return {String} The trimmed field label, or empty string if not defined
509 trimLabelSeparator: function() {
511 separator
= me
.labelSeparator
,
512 label
= me
.fieldLabel
|| '',
513 lastChar
= label
.substr(label
.length
- 1);
515 // if the last char is the same as the label separator then slice it off otherwise just return label value
516 return lastChar
=== separator
? label
.slice(0, -1) : label
;
520 * Returns the label for the field. Defaults to simply returning the {@link #fieldLabel} config. Can be overridden
521 * to provide a custom generated label.
523 * @return {String} The configured field label, or empty string if not defined
525 getFieldLabel: function() {
526 return this.trimLabelSeparator();
530 * Set the label of this field.
531 * @param {String} label The new label. The {@link #labelSeparator} will be automatically appended to the label
534 setFieldLabel: function(label
){
538 separator
= me
.labelSeparator
,
539 labelEl
= me
.labelEl
,
540 errorWrapEl
= me
.errorWrapEl
,
541 sideLabel
= (me
.labelAlign
!== 'top'),
542 noLabelCls
= me
.noLabelCls
,
543 errorWrapUnderSideLabelCls
= me
.errorWrapUnderSideLabelCls
;
545 me
.fieldLabel
= label
;
547 if (Ext
.isEmpty(label
) && me
.hideEmptyLabel
) {
548 me
.addCls(noLabelCls
);
549 if (sideLabel
&& errorWrapEl
) {
550 errorWrapEl
.removeCls(errorWrapUnderSideLabelCls
);
554 label
= me
.trimLabelSeparator() + separator
;
556 labelEl
.dom
.firstChild
.innerHTML
= label
;
557 me
.removeCls(noLabelCls
);
558 if (sideLabel
&& errorWrapEl
) {
559 errorWrapEl
.addCls(errorWrapUnderSideLabelCls
);
566 setHideLabel: function(hideLabel
) {
569 if (hideLabel
!== me
.hideLabel
) {
570 me
.hideLabel
= hideLabel
;
572 me
[hideLabel
? 'addCls' : 'removeCls'](me
.noLabelCls
);
578 setHideEmptyLabel: function(hideEmptyLabel
) {
582 if (hideEmptyLabel
!== me
.hideEmptyLabel
) {
583 me
.hideEmptyLabel
= hideEmptyLabel
;
584 if (me
.rendered
&& !me
.hideLabel
) {
585 hide
= hideEmptyLabel
&& !me
.getFieldLabel();
586 me
[hide
? 'addCls' : 'removeCls'](me
.noLabelCls
);
592 getInsertionRenderData: function (data
, names
) {
593 var i
= names
.length
,
601 if (typeof value
!== 'string') {
602 if (!value
.isTemplate
) {
603 value
= Ext
.XTemplate
.getTpl(this, name
);
605 value
= value
.apply(data
);
609 data
[name
] = value
|| '';
616 * Generates the arguments for the field decorations {@link #labelableRenderTpl
617 * rendering template}.
618 * @param {Object} data optional object to use as the base data object. If provided,
619 * this method will add properties to the base object instead of creating a new one.
620 * @return {Object} The template arguments
623 getLabelableRenderData: function() {
625 labelAlign
= me
.labelAlign
,
626 topLabel
= (labelAlign
=== 'top'),
627 rightLabel
= (labelAlign
=== 'right'),
628 sideError
= (me
.msgTarget
=== 'side'),
629 underError
= (me
.msgTarget
=== 'under'),
630 errorMsgCls
= me
.errorMsgCls
,
631 labelPad
= me
.labelPad
,
632 labelWidth
= me
.labelWidth
,
633 labelClsExtra
= me
.labelClsExtra
|| '',
634 errorWrapExtraCls
= sideError
? me
.errorWrapSideCls
: me
.errorWrapUnderCls
,
636 labelInnerStyle
= '',
637 labelVisible
= me
.hasVisibleLabel(),
638 autoFitErrors
= me
.autoFitErrors
,
639 defaultBodyWidth
= me
.defaultBodyWidth
,
643 labelClsExtra
+= ' ' + me
.topLabelCls
;
645 labelInnerStyle
= 'padding-bottom:' + labelPad
+ 'px;';
647 if (sideError
&& !autoFitErrors
) {
648 labelClsExtra
+= ' ' + me
.topLabelSideErrorCls
;
652 labelClsExtra
+= ' ' + me
.rightLabelCls
;
655 labelStyle
+= me
.getHorizontalPaddingStyle() + labelPad
+ 'px;';
657 labelStyle
+= 'width:' + (labelWidth
+ (labelPad
? labelPad
: 0)) + 'px;';
658 // inner label needs width as well so that setting width on the outside
659 // that is smaller than the natural width, will be ensured to take width
660 // away from the body, and not the label.
661 labelInnerStyle
= 'width:' + labelWidth
+ 'px';
665 if (!topLabel
&& underError
) {
666 errorWrapExtraCls
+= ' ' + me
.errorWrapUnderSideLabelCls
;
670 if (defaultBodyWidth
) {
671 // This is here to support textfield's deprecated "size" config
672 bodyStyle
= 'min-width:' + defaultBodyWidth
+ 'px;max-width:' +
673 defaultBodyWidth
+ 'px;';
678 inputId
: me
.getInputId(),
679 labelCls
: me
.labelCls
,
680 labelClsExtra
: labelClsExtra
,
681 labelStyle
: labelStyle
+ (me
.labelStyle
|| ''),
682 labelInnerStyle
: labelInnerStyle
,
683 labelInnerCls
: me
.labelInnerCls
,
684 unselectableCls
: Ext
.Element
.unselectableCls
,
685 bodyStyle
: bodyStyle
,
686 baseBodyCls
: me
.baseBodyCls
,
687 fieldBodyCls
: me
.fieldBodyCls
,
688 extraFieldBodyCls
: me
.extraFieldBodyCls
,
689 errorWrapCls
: me
.errorWrapCls
,
690 errorWrapExtraCls
: errorWrapExtraCls
,
691 renderError
: sideError
|| underError
,
692 invalidMsgCls
: sideError
? me
.invalidIconCls
: underError
? me
.invalidUnderCls
: '',
693 errorMsgCls
: errorMsgCls
,
694 growCls
: me
.grow
? me
.growCls
: '',
695 tipAnchorTarget
: me
.id
+ '-inputEl',
696 errorWrapStyle
: (sideError
&& !autoFitErrors
) ?
697 'visibility:hidden' : 'display:none',
698 fieldLabel
: me
.getFieldLabel(),
699 labelSeparator
: me
.labelSeparator
702 me
.getInsertionRenderData(data
, me
.labelableInsertions
);
708 getHorizontalPaddingStyle: function() {
709 return 'padding-right:';
712 beforeLabelRender: function() {
714 me
.setFieldDefaults(me
.getInherited().fieldDefaults
);
715 if (me
.ownerLayout
) {
716 me
.addCls(Ext
.baseCSSPrefix
+ me
.ownerLayout
.type
+ '-form-item');
718 if (!me
.hasVisibleLabel()) {
719 me
.addCls(me
.noLabelCls
);
723 onLabelRender: function() {
726 ExtElement
= Ext
.Element
,
727 errorWrapEl
= me
.errorWrapEl
,
731 errorWrapEl
.setVisibilityMode((me
.msgTarget
=== 'side' && !me
.autoFitErrors
) ?
732 ExtElement
.VISIBILITY
: ExtElement
.DISPLAY
);
735 if (me
.extraMargins
) {
736 margins
= me
.el
.getMargin();
737 for (side
in margins
) {
738 if (margins
.hasOwnProperty(side
)) {
739 style
['margin-' + side
] = (margins
[side
] + me
.extraMargins
[side
]) + 'px';
742 me
.el
.setStyle(style
);
747 * Checks if the field has a visible label
748 * @return {Boolean} True if the field has a visible label
750 hasVisibleLabel: function(){
751 if (this.hideLabel
) {
754 return !(this.hideEmptyLabel
&& !this.getFieldLabel());
758 * Gets the markup to be inserted into the outer template's bodyEl. Defaults to empty string, should be implemented
759 * by classes including this mixin as needed.
760 * @return {String} The markup to be inserted
763 getSubTplMarkup: function() {
768 * Get the input id, if any, for this component. This is used as the "for" attribute on the label element.
769 * Implementing subclasses may also use this as e.g. the id for their own input element.
770 * @return {String} The input id
772 getInputId: function() {
777 * Gets the active error message for this component, if any. This does not trigger validation on its own, it merely
778 * returns any message that the component may already hold.
779 * @return {String} The active error message on the component; if there is no error, an empty string is returned.
781 getActiveError : function() {
782 return this.activeError
|| '';
786 * Tells whether the field currently has an active error message. This does not trigger validation on its own, it
787 * merely looks for any message that the component may already hold.
790 hasActiveError: function() {
791 return !!this.getActiveError();
795 * Sets the active error message to the given string. This replaces the entire error message contents with the given
796 * string. Also see {@link #setActiveErrors} which accepts an Array of messages and formats them according to the
797 * {@link #activeErrorsTpl}. Note that this only updates the error message element's text and attributes, you'll
798 * have to call doComponentLayout to actually update the field's layout to match. If the field extends {@link
799 * Ext.form.field.Base} you should call {@link Ext.form.field.Base#markInvalid markInvalid} instead.
800 * @param {String} msg The error message
802 setActiveError: function(msg
) {
803 this.setActiveErrors(msg
);
807 * Gets an Array of any active error messages currently applied to the field. This does not trigger validation on
808 * its own, it merely returns any messages that the component may already hold.
809 * @return {String[]} The active error messages on the component; if there are no errors, an empty Array is
812 getActiveErrors: function() {
813 return this.activeErrors
|| [];
817 * Set the active error message to an Array of error messages. The messages are formatted into a single message
818 * string using the {@link #activeErrorsTpl}. Also see {@link #setActiveError} which allows setting the entire error
819 * contents with a single string. Note that this only updates the error message element's text and attributes,
820 * you'll have to call doComponentLayout to actually update the field's layout to match. If the field extends
821 * {@link Ext.form.field.Base} you should call {@link Ext.form.field.Base#markInvalid markInvalid} instead.
822 * @param {String[]} errors The error messages
824 setActiveErrors: function(errors
) {
826 errorWrapEl
= me
.errorWrapEl
,
827 msgTarget
= me
.msgTarget
,
828 isSide
= msgTarget
=== 'side',
829 isQtip
= msgTarget
=== 'qtip',
830 actionEl
, activeError
, tpl
, targetEl
;
832 errors
= Ext
.Array
.from(errors
);
833 tpl
= me
.getTpl('activeErrorsTpl');
835 me
.activeErrors
= errors
;
836 activeError
= me
.activeError
= tpl
.apply({
837 fieldLabel
: me
.fieldLabel
,
839 listCls
: Ext
.baseCSSPrefix
+ 'list-plain'
842 me
.renderActiveError();
845 actionEl
= me
.getActionEl();
848 me
.errorEl
.dom
.setAttribute('data-errorqtip', activeError
);
851 actionEl
.dom
.setAttribute('data-errorqtip', activeError
);
853 else if (msgTarget
=== 'title') {
854 actionEl
.dom
.setAttribute('title', activeError
);
857 // If msgTarget is title, setting an alert is redundant for ARIA purposes
858 if (msgTarget
!== 'title') {
859 me
.ariaErrorEl
.dom
.innerHTML
= errors
.join('. ');
860 actionEl
.dom
.setAttribute('aria-describedby', me
.ariaErrorEl
.id
);
863 if (isSide
|| isQtip
) {
864 Ext
.form
.Labelable
.initTip();
867 if (!me
.msgTargets
[msgTarget
]) {
868 targetEl
= Ext
.get(msgTarget
);
871 targetEl
.dom
.innerHTML
= activeError
;
877 errorWrapEl
.setVisible(errors
.length
> 0);
878 if (isSide
&& me
.autoFitErrors
) {
879 me
.labelEl
.addCls(me
.topLabelSideErrorCls
);
886 * Clears the active error message(s). Note that this only clears the error message element's text and attributes,
887 * you'll have to call doComponentLayout to actually update the field's layout to match. If the field extends {@link
888 * Ext.form.field.Base} you should call {@link Ext.form.field.Base#clearInvalid clearInvalid} instead.
890 unsetActiveError: function() {
892 errorWrapEl
= me
.errorWrapEl
,
893 msgTarget
= me
.msgTarget
,
894 restoreDisplay
= me
.restoreDisplay
,
897 if (me
.hasActiveError()) {
898 delete me
.activeError
;
899 delete me
.activeErrors
;
900 me
.renderActiveError();
903 actionEl
= me
.getActionEl();
905 if (msgTarget
=== 'qtip') {
906 actionEl
.dom
.removeAttribute('data-errorqtip');
908 else if (msgTarget
=== 'title') {
909 actionEl
.dom
.removeAttribute('title');
912 if (msgTarget
!== 'title') {
913 me
.ariaErrorEl
.dom
.innerHTML
= '';
914 actionEl
.dom
.removeAttribute('aria-describedby');
917 if (!me
.msgTargets
[msgTarget
]) {
918 targetEl
= Ext
.get(msgTarget
);
921 targetEl
.dom
.innerHTML
= '';
927 if (msgTarget
=== 'side' && me
.autoFitErrors
) {
928 me
.labelEl
.removeCls(me
.topLabelSideErrorCls
);
932 // IE8 hack for https://sencha.jira.com/browse/EXTJS-17536.
933 // Need to force a relayout of the display:table form item.
934 // TODO: Remove when IE8 retires.
935 if (restoreDisplay
) {
936 me
.el
.dom
.style
.display
= 'block';
944 doRestoreDisplay: function() {
945 // IE8 hack for https://sencha.jira.com/browse/EXTJS-17536.
946 // Need to force a relayout of the display:table form item.
947 // TODO: Remove this method when IE8 retires.
950 el
.dom
.style
.display
= '';
956 * Updates the rendered DOM to match the current activeError. This only updates the content and
957 * attributes, you'll have to call doComponentLayout to actually update the display.
959 renderActiveError: function() {
961 activeError
= me
.getActiveError(),
962 hasError
= !!activeError
;
964 if (activeError
!== me
.lastActiveError
) {
965 me
.lastActiveError
= activeError
;
966 me
.fireEvent('errorchange', me
, activeError
);
969 if (me
.rendered
&& !me
.destroyed
&& !me
.preventMark
) {
970 me
.toggleInvalidCls(hasError
);
971 // Update the errorEl (There will only be one if msgTarget is 'side' or 'under') with the error message text
973 me
.errorEl
.dom
.innerHTML
= activeError
;
980 * Add/remove invalid class(es)
981 * @param {Boolean} hasError
983 toggleInvalidCls: function(hasError
) {
984 this.el
[hasError
? 'addCls' : 'removeCls'](this.invalidCls
);
988 * Applies a set of default configuration values to this Labelable instance. For each of the properties in the given
989 * object, check if this component hasOwnProperty that config; if not then it's inheriting a default value from its
990 * prototype and we should apply the default value.
991 * @param {Object} defaults The defaults to apply to the object.
993 setFieldDefaults: function(defaults
) {
996 for (key
in defaults
) {
997 if (!this.hasOwnProperty(key
)) {
998 this[key
] = defaults
[key
];
1003 if (Ext
.supports
.Touch
) {
1004 this.prototype.msgTarget
= 'side';