3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * A {@link Ext.form.FieldContainer field container} which has a specialized layout for arranging
17 * {@link Ext.form.field.Checkbox} controls into columns, and provides convenience
18 * {@link Ext.form.field.Field} methods for {@link #getValue getting}, {@link #setValue setting},
19 * and {@link #validate validating} the group of checkboxes as a whole.
23 * Individual checkbox fields themselves have no default validation behavior, but
24 * sometimes you want to require a user to select at least one of a group of checkboxes. CheckboxGroup
25 * allows this by setting the config `{@link #allowBlank}:false`; when the user does not check at
26 * least one of the checkboxes, the entire group will be highlighted as invalid and the
27 * {@link #blankText error message} will be displayed according to the {@link #msgTarget} config.
31 * The default layout for CheckboxGroup makes it easy to arrange the checkboxes into
32 * columns; see the {@link #columns} and {@link #vertical} config documentation for details. You may also
33 * use a completely different layout by setting the {@link #layout} to one of the other supported layout
34 * types; for instance you may wish to use a custom arrangement of hbox and vbox containers. In that case
35 * the checkbox components at any depth will still be managed by the CheckboxGroup's validation.
38 * Ext.create('Ext.form.Panel', {
39 * title: 'Checkbox Group',
43 * renderTo: Ext.getBody(),
45 * xtype: 'checkboxgroup',
46 * fieldLabel: 'Two Columns',
47 * // Arrange radio buttons into two columns, distributed vertically
51 * { boxLabel: 'Item 1', name: 'rb', inputValue: '1' },
52 * { boxLabel: 'Item 2', name: 'rb', inputValue: '2', checked: true },
53 * { boxLabel: 'Item 3', name: 'rb', inputValue: '3' },
54 * { boxLabel: 'Item 4', name: 'rb', inputValue: '4' },
55 * { boxLabel: 'Item 5', name: 'rb', inputValue: '5' },
56 * { boxLabel: 'Item 6', name: 'rb', inputValue: '6' }
61 Ext.define('Ext.form.CheckboxGroup', {
62 extend:'Ext.form.FieldContainer',
64 field: 'Ext.form.field.Field'
66 alias: 'widget.checkboxgroup',
67 requires: ['Ext.layout.container.CheckboxGroup', 'Ext.form.field.Base'],
75 * @cfg {Ext.form.field.Checkbox[]/Object[]} items
76 * An Array of {@link Ext.form.field.Checkbox Checkbox}es or Checkbox config objects to arrange in the group.
80 * @cfg {String/Number/Number[]} columns
81 * Specifies the number of columns to use when displaying grouped checkbox/radio controls using automatic layout.
82 * This config can take several types of values:
84 * - 'auto' - The controls will be rendered one per column on one row and the width of each column will be evenly
85 * distributed based on the width of the overall field container. This is the default.
86 * - Number - If you specific a number (e.g., 3) that number of columns will be created and the contained controls
87 * will be automatically distributed based on the value of {@link #vertical}.
88 * - Array - You can also specify an array of column widths, mixing integer (fixed width) and float (percentage
89 * width) values as needed (e.g., [100, .25, .75]). Any integer values will be rendered first, then any float
90 * values will be calculated as a percentage of the remaining space. Float values do not have to add up to 1
91 * (100%) although if you want the controls to take up the entire field container you should do so.
96 * @cfg {Boolean} vertical
97 * True to distribute contained controls across columns, completely filling each column top to bottom before
98 * starting on the next column. The number of controls in each column will be automatically calculated to keep
99 * columns as even as possible. The default value is false, so that controls will be added to columns one at a time,
100 * completely filling each row left to right before starting on the next row.
105 * @cfg {Boolean} allowBlank
106 * False to validate that at least one item in the group is checked. If no items are selected at
107 * validation time, {@link #blankText} will be used as the error text.
112 * @cfg {String} blankText
113 * Error text to display if the {@link #allowBlank} validation fails
115 blankText : "You must select at least one item in this group",
118 defaultType : 'checkboxfield',
121 groupCls : Ext.baseCSSPrefix + 'form-check-group',
124 * @cfg {String} fieldBodyCls
125 * An extra CSS class to be applied to the body content element in addition to {@link #baseBodyCls}.
126 * Defaults to 'x-form-checkboxgroup-body'.
128 fieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body',
131 layout: 'checkboxgroup',
133 initComponent: function() {
140 * Initializes the field's value based on the initial config. If the {@link #value} config is specified then we use
141 * that to set the value; otherwise we initialize the originalValue by querying the values of all sub-checkboxes
142 * after they have been initialized.
145 initValue: function() {
148 me.originalValue = me.lastValue = valueCfg || me.getValue();
150 me.setValue(valueCfg);
155 * When a checkbox is added to the group, monitor it for changes
156 * @param {Object} field
159 onFieldAdded: function(field) {
161 if (field.isCheckbox) {
162 me.mon(field, 'change', me.checkChange, me);
164 me.callParent(arguments);
167 onFieldRemoved: function(field) {
169 if (field.isCheckbox) {
170 me.mun(field, 'change', me.checkChange, me);
172 me.callParent(arguments);
175 // private override - the group value is a complex object, compare using object serialization
176 isEqual: function(value1, value2) {
177 var toQueryString = Ext.Object.toQueryString;
178 return toQueryString(value1) === toQueryString(value2);
182 * Runs CheckboxGroup's validations and returns an array of any errors. The only error by default is if allowBlank
183 * is set to true and no items are checked.
184 * @return {String[]} Array of all validation errors
186 getErrors: function() {
188 if (!this.allowBlank && Ext.isEmpty(this.getChecked())) {
189 errors.push(this.blankText);
195 * @private Returns all checkbox components within the container
197 getBoxes: function() {
198 return this.query('[isCheckbox]');
202 * @private Convenience function which calls the given function for every checkbox in the group
203 * @param {Function} fn The function to call
204 * @param {Object} scope (Optional) scope object
206 eachBox: function(fn, scope) {
207 Ext.Array.forEach(this.getBoxes(), fn, scope || this);
211 * Returns an Array of all checkboxes in the container which are currently checked
212 * @return {Ext.form.field.Checkbox[]} Array of Ext.form.field.Checkbox components
214 getChecked: function() {
215 return Ext.Array.filter(this.getBoxes(), function(cb) {
216 return cb.getValue();
222 return Ext.Array.some(this.getBoxes(), function(cb) {
228 setReadOnly: function(readOnly) {
229 this.eachBox(function(cb) {
230 cb.setReadOnly(readOnly);
232 this.readOnly = readOnly;
236 * Resets the checked state of all {@link Ext.form.field.Checkbox checkboxes} in the group to their originally
237 * loaded values and clears any validation messages.
238 * See {@link Ext.form.Basic}.{@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad}
242 hadError = me.hasActiveError(),
243 preventMark = me.preventMark;
244 me.preventMark = true;
245 me.batchChanges(function() {
246 me.eachBox(function(cb) {
250 me.preventMark = preventMark;
251 me.unsetActiveError();
253 me.doComponentLayout();
258 resetOriginalValue: function() {
259 // Defer resetting of originalValue until after all sub-checkboxes have been reset so we get
260 // the correct data from getValue()
261 Ext.defer(function() {
268 * Sets the value(s) of all checkboxes in the group. The expected format is an Object of name-value pairs
269 * corresponding to the names of the checkboxes in the group. Each pair can have either a single or multiple values:
271 * - A single Boolean or String value will be passed to the `setValue` method of the checkbox with that name.
272 * See the rules in {@link Ext.form.field.Checkbox#setValue} for accepted values.
273 * - An Array of String values will be matched against the {@link Ext.form.field.Checkbox#inputValue inputValue}
274 * of checkboxes in the group with that name; those checkboxes whose inputValue exists in the array will be
275 * checked and others will be unchecked.
277 * If a checkbox's name is not in the mapping at all, it will be unchecked.
281 * var myCheckboxGroup = new Ext.form.CheckboxGroup({
285 * boxLabel: 'Single 1'
288 * boxLabel: 'Single 2'
291 * boxLabel: 'Single 3'
294 * boxLabel: 'Grouped 1'
295 * inputValue: 'value1'
298 * boxLabel: 'Grouped 2'
299 * inputValue: 'value2'
302 * boxLabel: 'Grouped 3'
303 * inputValue: 'value3'
307 * myCheckboxGroup.setValue({
310 * cbGroup: ['value1', 'value3']
313 * The above code will cause the checkbox named 'cb1' to be checked, as well as the first and third checkboxes named
314 * 'cbGroup'. The other three checkboxes will be unchecked.
316 * @param {Object} value The mapping of checkbox names to values.
317 * @return {Ext.form.CheckboxGroup} this
319 setValue: function(value) {
321 me.batchChanges(function() {
322 me.eachBox(function(cb) {
323 var name = cb.getName(),
325 if (value && name in value) {
326 if (Ext.isArray(value[name])) {
327 cbValue = Ext.Array.contains(value[name], cb.inputValue);
329 // single value, let the checkbox's own setValue handle conversion
330 cbValue = value[name];
333 cb.setValue(cbValue);
341 * Returns an object containing the values of all checked checkboxes within the group. Each key-value pair in the
342 * object corresponds to a checkbox {@link Ext.form.field.Checkbox#name name}. If there is only one checked checkbox
343 * with a particular name, the value of that pair will be the String {@link Ext.form.field.Checkbox#inputValue
344 * inputValue} of that checkbox. If there are multiple checked checkboxes with that name, the value of that pair
345 * will be an Array of the selected inputValues.
347 * The object format returned from this method can also be passed directly to the {@link #setValue} method.
349 * NOTE: In Ext 3, this method returned an array of Checkbox components; this was changed to make it more consistent
350 * with other field components and with the {@link #setValue} argument signature. If you need the old behavior in
351 * Ext 4+, use the {@link #getChecked} method instead.
353 getValue: function() {
355 this.eachBox(function(cb) {
356 var name = cb.getName(),
357 inputValue = cb.inputValue,
360 if (name in values) {
361 bucket = values[name];
362 if (!Ext.isArray(bucket)) {
363 bucket = values[name] = [bucket];
365 bucket.push(inputValue);
367 values[name] = inputValue;
375 * Don't return any data for submit; the form will get the info from the individual checkboxes themselves.
377 getSubmitData: function() {
382 * Don't return any data for the model; the form will get the info from the individual checkboxes themselves.
384 getModelData: function() {
388 validate: function() {
390 errors = me.getErrors(),
391 isValid = Ext.isEmpty(errors),
392 wasValid = !me.hasActiveError();
395 me.unsetActiveError();
397 me.setActiveError(errors);
399 if (isValid !== wasValid) {
400 me.fireEvent('validitychange', me, isValid);
401 me.doComponentLayout();
409 this.borrow(Ext.form.field.Base, ['markInvalid', 'clearInvalid']);