/ext-4.1.0_b3/src/form/field/Checkbox.js
JavaScript | 470 lines | 172 code | 51 blank | 247 comment | 19 complexity | cd270de0ca3b0248fcd8b224f1e77bd5 MD5 | raw file
1/**
2 * @docauthor Robert Dougan <rob@sencha.com>
3 *
4 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields. Also serves as a
5 * parent class for {@link Ext.form.field.Radio radio buttons}.
6 *
7 * # Labeling
8 *
9 * In addition to the {@link Ext.form.Labelable standard field labeling options}, checkboxes
10 * may be given an optional {@link #boxLabel} which will be displayed immediately after checkbox. Also see
11 * {@link Ext.form.CheckboxGroup} for a convenient method of grouping related checkboxes.
12 *
13 * # Values
14 *
15 * The main value of a checkbox is a boolean, indicating whether or not the checkbox is checked.
16 * The following values will check the checkbox:
17 *
18 * - `true`
19 * - `'true'`
20 * - `'1'`
21 * - `'on'`
22 *
23 * Any other value will uncheck the checkbox.
24 *
25 * In addition to the main boolean value, you may also specify a separate {@link #inputValue}. This will be
26 * sent as the parameter value when the form is {@link Ext.form.Basic#submit submitted}. You will want to set
27 * this value if you have multiple checkboxes with the same {@link #name}. If not specified, the value `on`
28 * will be used.
29 *
30 * # Example usage
31 *
32 * @example
33 * Ext.create('Ext.form.Panel', {
34 * bodyPadding: 10,
35 * width: 300,
36 * title: 'Pizza Order',
37 * items: [
38 * {
39 * xtype: 'fieldcontainer',
40 * fieldLabel: 'Toppings',
41 * defaultType: 'checkboxfield',
42 * items: [
43 * {
44 * boxLabel : 'Anchovies',
45 * name : 'topping',
46 * inputValue: '1',
47 * id : 'checkbox1'
48 * }, {
49 * boxLabel : 'Artichoke Hearts',
50 * name : 'topping',
51 * inputValue: '2',
52 * checked : true,
53 * id : 'checkbox2'
54 * }, {
55 * boxLabel : 'Bacon',
56 * name : 'topping',
57 * inputValue: '3',
58 * id : 'checkbox3'
59 * }
60 * ]
61 * }
62 * ],
63 * bbar: [
64 * {
65 * text: 'Select Bacon',
66 * handler: function() {
67 * Ext.getCmp('checkbox3').setValue(true);
68 * }
69 * },
70 * '-',
71 * {
72 * text: 'Select All',
73 * handler: function() {
74 * Ext.getCmp('checkbox1').setValue(true);
75 * Ext.getCmp('checkbox2').setValue(true);
76 * Ext.getCmp('checkbox3').setValue(true);
77 * }
78 * },
79 * {
80 * text: 'Deselect All',
81 * handler: function() {
82 * Ext.getCmp('checkbox1').setValue(false);
83 * Ext.getCmp('checkbox2').setValue(false);
84 * Ext.getCmp('checkbox3').setValue(false);
85 * }
86 * }
87 * ],
88 * renderTo: Ext.getBody()
89 * });
90 */
91Ext.define('Ext.form.field.Checkbox', {
92 extend: 'Ext.form.field.Base',
93 alias: ['widget.checkboxfield', 'widget.checkbox'],
94 alternateClassName: 'Ext.form.Checkbox',
95 requires: ['Ext.XTemplate', 'Ext.form.CheckboxManager' ],
96
97 componentLayout: 'field',
98
99 childEls: [
100 /**
101 * @property {Ext.Element} boxLabelEl
102 * A reference to the label element created for the {@link #boxLabel}. Only present if the component has been
103 * rendered and has a boxLabel configured.
104 */
105 'boxLabelEl'
106 ],
107
108 // note: {id} here is really {inputId}, but {cmpId} is available
109 fieldSubTpl: [
110 '<tpl if="boxLabel && boxLabelAlign == \'before\'">',
111 '{beforeBoxLabelTpl}',
112 '<label id="{cmpId}-boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">',
113 '{beforeBoxLabelTextTpl}',
114 '{boxLabel}',
115 '{afterBoxLabelTextTpl}',
116 '</label>',
117 '{afterBoxLabelTpl}',
118 '</tpl>',
119 // Creates not an actual checkbox, but a button which is given aria role="checkbox" (If ARIA is required) and
120 // styled with a custom checkbox image. This allows greater control and consistency in
121 // styling, and using a button allows it to gain focus and handle keyboard nav properly.
122 '<input type="button" id="{id}" {inputAttrTpl}',
123 '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
124 '<tpl if="disabled"> disabled="disabled"</tpl>',
125 '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
126 ' class="{fieldCls} {typeCls}" autocomplete="off" hidefocus="true" />',
127 '<tpl if="boxLabel && boxLabelAlign == \'after\'">',
128 '{beforeBoxLabelTpl}',
129 '<label id="{cmpId}-boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">',
130 '{beforeBoxLabelTextTpl}',
131 '{boxLabel}',
132 '{afterBoxLabelTextTpl}',
133 '</label>',
134 '{afterBoxLabelTpl}',
135 '</tpl>',
136 {
137 disableFormats: true,
138 compiled: true
139 }
140 ],
141
142 subTplInsertions: [
143 /**
144 * @cfg {String/Array/Ext.XTemplate} beforeBoxLabelTpl
145 * An optional string or `XTemplate` configuration to insert in the field markup
146 * before the box label element. If an `XTemplate` is used, the component's
147 * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
148 */
149 'beforeBoxLabelTpl',
150
151 /**
152 * @cfg {String/Array/Ext.XTemplate} afterBoxLabelTpl
153 * An optional string or `XTemplate` configuration to insert in the field markup
154 * after the box label element. If an `XTemplate` is used, the component's
155 * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
156 */
157 'afterBoxLabelTpl',
158
159 /**
160 * @cfg {String/Array/Ext.XTemplate} beforeBoxLabelTextTpl
161 * An optional string or `XTemplate` configuration to insert in the field markup
162 * before the box label text. If an `XTemplate` is used, the component's
163 * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
164 */
165 'beforeBoxLabelTextTpl',
166
167 /**
168 * @cfg {String/Array/Ext.XTemplate} afterBoxLabelTextTpl
169 * An optional string or `XTemplate` configuration to insert in the field markup
170 * after the box label text. If an `XTemplate` is used, the component's
171 * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
172 */
173 'afterBoxLabelTextTpl',
174
175 /**
176 * @cfg {String/Array/Ext.XTemplate} boxLabelAttrTpl
177 * An optional string or `XTemplate` configuration to insert in the field markup
178 * inside the box label element (as attributes). If an `XTemplate` is used, the component's
179 * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
180 */
181 'boxLabelAttrTpl',
182
183 // inherited
184 'inputAttrTpl'
185 ],
186
187 /*
188 * @property {Boolean} isCheckbox
189 * `true` in this class to identify an objact as an instantiated Checkbox, or subclass thereof.
190 */
191 isCheckbox: true,
192
193 /**
194 * @cfg {String} [focusCls='x-form-cb-focus']
195 * The CSS class to use when the checkbox receives focus
196 */
197 focusCls: Ext.baseCSSPrefix + 'form-cb-focus',
198
199 /**
200 * @cfg {String} [fieldCls='x-form-field']
201 * The default CSS class for the checkbox
202 */
203
204 /**
205 * @cfg {String} [fieldBodyCls='x-form-cb-wrap']
206 * An extra CSS class to be applied to the body content element in addition to {@link #fieldBodyCls}.
207 * .
208 */
209 fieldBodyCls: Ext.baseCSSPrefix + 'form-cb-wrap',
210
211 /**
212 * @cfg {Boolean} checked
213 * true if the checkbox should render initially checked
214 */
215 checked: false,
216
217 /**
218 * @cfg {String} [checkedCls='x-form-cb-checked']
219 * The CSS class added to the component's main element when it is in the checked state.
220 */
221 checkedCls: Ext.baseCSSPrefix + 'form-cb-checked',
222
223 /**
224 * @cfg {String} boxLabel
225 * An optional text label that will appear next to the checkbox. Whether it appears before or after the checkbox is
226 * determined by the {@link #boxLabelAlign} config.
227 */
228
229 /**
230 * @cfg {String} [boxLabelCls='x-form-cb-label']
231 * The CSS class to be applied to the {@link #boxLabel} element
232 */
233 boxLabelCls: Ext.baseCSSPrefix + 'form-cb-label',
234
235 /**
236 * @cfg {String} boxLabelAlign
237 * The position relative to the checkbox where the {@link #boxLabel} should appear. Recognized values are 'before'
238 * and 'after'.
239 */
240 boxLabelAlign: 'after',
241
242 /**
243 * @cfg {String} inputValue
244 * The value that should go into the generated input element's value attribute and should be used as the parameter
245 * value when submitting as part of a form.
246 */
247 inputValue: 'on',
248
249 /**
250 * @cfg {String} uncheckedValue
251 * If configured, this will be submitted as the checkbox's value during form submit if the checkbox is unchecked. By
252 * default this is undefined, which results in nothing being submitted for the checkbox field when the form is
253 * submitted (the default behavior of HTML checkboxes).
254 */
255
256 /**
257 * @cfg {Function} handler
258 * A function called when the {@link #checked} value changes (can be used instead of handling the {@link #change
259 * change event}).
260 * @cfg {Ext.form.field.Checkbox} handler.checkbox The Checkbox being toggled.
261 * @cfg {Boolean} handler.checked The new checked state of the checkbox.
262 */
263
264 /**
265 * @cfg {Object} scope
266 * An object to use as the scope ('this' reference) of the {@link #handler} function.
267 *
268 * Defaults to this Checkbox.
269 */
270
271 // private overrides
272 checkChangeEvents: [],
273 inputType: 'checkbox',
274
275 // private
276 onRe: /^on$/i,
277
278 initComponent: function() {
279 this.callParent(arguments);
280 this.getManager().add(this);
281 },
282
283 initValue: function() {
284 var me = this,
285 checked = !!me.checked;
286
287 /**
288 * @property {Object} originalValue
289 * The original value of the field as configured in the {@link #checked} configuration, or as loaded by the last
290 * form load operation if the form's {@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad} setting is `true`.
291 */
292 me.originalValue = me.lastValue = checked;
293
294 // Set the initial checked state
295 me.setValue(checked);
296 },
297
298 getElConfig: function() {
299 var me = this;
300
301 // Add the checked class if this begins checked
302 if (me.isChecked(me.rawValue, me.inputValue)) {
303 me.addCls(me.checkedCls);
304 }
305 return me.callParent();
306 },
307
308 getFieldStyle: function() {
309 return Ext.isObject(this.fieldStyle) ? Ext.DomHelper.generateStyles(this.fieldStyle) : this.fieldStyle ||'';
310 },
311
312 getSubTplData: function() {
313 var me = this;
314 return Ext.apply(me.callParent(), {
315 disabled : me.readOnly || me.disabled,
316 boxLabel : me.boxLabel,
317 boxLabelCls : me.boxLabelCls,
318 boxLabelAlign : me.boxLabelAlign
319 });
320 },
321
322 initEvents: function() {
323 var me = this;
324 me.callParent();
325 me.mon(me.inputEl, 'click', me.onBoxClick, me);
326 },
327
328 /**
329 * @private Handle click on the checkbox button
330 */
331 onBoxClick: function(e) {
332 var me = this;
333 if (!me.disabled && !me.readOnly) {
334 this.setValue(!this.checked);
335 }
336 },
337
338 /**
339 * Returns the checked state of the checkbox.
340 * @return {Boolean} True if checked, else false
341 */
342 getRawValue: function() {
343 return this.checked;
344 },
345
346 /**
347 * Returns the checked state of the checkbox.
348 * @return {Boolean} True if checked, else false
349 */
350 getValue: function() {
351 return this.checked;
352 },
353
354 /**
355 * Returns the submit value for the checkbox which can be used when submitting forms.
356 * @return {String} If checked the {@link #inputValue} is returned; otherwise the {@link #uncheckedValue}
357 * (or null if the latter is not configured).
358 */
359 getSubmitValue: function() {
360 var unchecked = this.uncheckedValue,
361 uncheckedVal = Ext.isDefined(unchecked) ? unchecked : null;
362 return this.checked ? this.inputValue : uncheckedVal;
363 },
364
365 isChecked: function(rawValue, inputValue) {
366 return (rawValue === true || rawValue === 'true' || rawValue === '1' || rawValue === 1 ||
367 (((Ext.isString(rawValue) || Ext.isNumber(rawValue)) && inputValue) ? rawValue == inputValue : this.onRe.test(rawValue)));
368 },
369
370 /**
371 * Sets the checked state of the checkbox.
372 *
373 * @param {Boolean/String/Number} value The following values will check the checkbox:
374 * `true, 'true', '1', 1, or 'on'`, as well as a String that matches the {@link #inputValue}.
375 * Any other value will uncheck the checkbox.
376 * @return {Boolean} the new checked state of the checkbox
377 */
378 setRawValue: function(value) {
379 var me = this,
380 inputEl = me.inputEl,
381 checked = me.isChecked(value, me.inputValue);
382
383 if (inputEl) {
384 me[checked ? 'addCls' : 'removeCls'](me.checkedCls);
385 }
386
387 me.checked = me.rawValue = checked;
388 return checked;
389 },
390
391 /**
392 * Sets the checked state of the checkbox, and invokes change detection.
393 * @param {Boolean/String} checked The following values will check the checkbox: `true, 'true', '1', or 'on'`, as
394 * well as a String that matches the {@link #inputValue}. Any other value will uncheck the checkbox.
395 * @return {Ext.form.field.Checkbox} this
396 */
397 setValue: function(checked) {
398 var me = this,
399 boxes, b, bLen, box;
400
401 // If an array of strings is passed, find all checkboxes in the group with the same name as this
402 // one and check all those whose inputValue is in the array, unchecking all the others. This is to
403 // facilitate setting values from Ext.form.Basic#setValues, but is not publicly documented as we
404 // don't want users depending on this behavior.
405 if (Ext.isArray(checked)) {
406 boxes = me.getManager().getByName(me.name).items,
407 bLen = boxes.length;
408
409 for (b = 0; b < bLen; b++) {
410 box = boxes[b];
411 box.setValue(Ext.Array.contains(checked, box.inputValue));
412 }
413 } else {
414 me.callParent(arguments);
415 }
416
417 return me;
418 },
419
420 // private
421 valueToRaw: function(value) {
422 // No extra conversion for checkboxes
423 return value;
424 },
425
426 /**
427 * @private
428 * Called when the checkbox's checked state changes. Invokes the {@link #handler} callback
429 * function if specified.
430 */
431 onChange: function(newVal, oldVal) {
432 var me = this,
433 handler = me.handler;
434 if (handler) {
435 handler.call(me.scope || me, me, newVal);
436 }
437 me.callParent(arguments);
438 },
439
440 // inherit docs
441 beforeDestroy: function(){
442 this.callParent();
443 this.getManager().removeAtKey(this.id);
444 },
445
446 // inherit docs
447 getManager: function() {
448 return Ext.form.CheckboxManager;
449 },
450
451 onEnable: function() {
452 var me = this,
453 inputEl = me.inputEl;
454 me.callParent();
455 if (inputEl) {
456 // Can still be disabled if the field is readOnly
457 inputEl.dom.disabled = me.readOnly;
458 }
459 },
460
461 setReadOnly: function(readOnly) {
462 var me = this,
463 inputEl = me.inputEl;
464 if (inputEl) {
465 // Set the button to disabled when readonly
466 inputEl.dom.disabled = !!readOnly || me.disabled;
467 }
468 me.callParent(arguments);
469 }
470});