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 {@link Ext.form.field.Field} methods
18 * for {@link #getValue getting}, {@link #setValue setting}, and {@link #validate validating} the group
19 * 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.
37 * {@img Ext.form.CheckboxGroup/Ext.form.CheckboxGroup.png Ext.form.CheckboxGroup component}
41 * Ext.create('Ext.form.Panel', {
42 * title: 'Checkbox Group',
46 * renderTo: Ext.getBody(),
48 * xtype: 'checkboxgroup',
49 * fieldLabel: 'Two Columns',
50 * // Arrange radio buttons into two columns, distributed vertically
54 * {boxLabel: 'Item 1', name: 'rb', inputValue: '1'},
55 * {boxLabel: 'Item 2', name: 'rb', inputValue: '2', checked: true},
56 * {boxLabel: 'Item 3', name: 'rb', inputValue: '3'},
57 * {boxLabel: 'Item 4', name: 'rb', inputValue: '4'},
58 * {boxLabel: 'Item 5', name: 'rb', inputValue: '5'},
59 * {boxLabel: 'Item 6', name: 'rb', inputValue: '6'}
65 Ext.define('Ext.form.CheckboxGroup', {
66 extend:'Ext.form.FieldContainer',
68 field: 'Ext.form.field.Field'
70 alias: 'widget.checkboxgroup',
71 requires: ['Ext.layout.container.CheckboxGroup', 'Ext.form.field.Base'],
79 * @cfg {Array} items An Array of {@link Ext.form.field.Checkbox Checkbox}es or Checkbox config objects
80 * to arrange in the group.
84 * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
85 * checkbox/radio controls using automatic layout. This config can take several types of values:
86 * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
87 * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
88 * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
89 * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
90 * <li><b>Array</b> : <p class="sub-desc">You can also specify an array of column widths, mixing integer
91 * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
92 * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
93 * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
94 * container you should do so.</p></li></ul>
99 * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
100 * top to bottom before starting on the next column. The number of controls in each column will be automatically
101 * calculated to keep columns as even as possible. The default value is false, so that controls will be added
102 * to columns one at a time, completely filling each row left to right before starting on the next row.
107 * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
108 * If no items are selected at validation time, {@link #blankText} will be used as the error text.
113 * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
114 * select at least one item in this group")
116 blankText : "You must select at least one item in this group",
119 defaultType : 'checkboxfield',
122 groupCls : Ext.baseCSSPrefix + 'form-check-group',
125 * @cfg {String} fieldBodyCls
126 * An extra CSS class to be applied to the body content element in addition to {@link #baseBodyCls}.
127 * Defaults to 'x-form-checkboxgroup-body'.
129 fieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body',
132 layout: 'checkboxgroup',
134 initComponent: function() {
142 * Initializes the field's value based on the initial config. If the {@link #value} config is specified
143 * then we use that to set the value; otherwise we initialize the originalValue by querying the values of
144 * all sub-checkboxes after they have been initialized.
146 initValue: function() {
149 me.originalValue = me.lastValue = valueCfg || me.getValue();
151 me.setValue(valueCfg);
157 * When a checkbox is added to the group, monitor it for changes
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
183 * is if allowBlank is set to true and no items are checked.
184 * @return {Array} 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 {Array} 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
237 * originally 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 * <p>Sets the value(s) of all checkboxes in the group. The expected format is an Object of
269 * name-value pairs corresponding to the names of the checkboxes in the group. Each pair can
270 * have either a single or multiple values:</p>
272 * <li>A single Boolean or String value will be passed to the <code>setValue</code> method of the
273 * checkbox with that name. See the rules in {@link Ext.form.field.Checkbox#setValue} for accepted values.</li>
274 * <li>An Array of String values will be matched against the {@link Ext.form.field.Checkbox#inputValue inputValue}
275 * of checkboxes in the group with that name; those checkboxes whose inputValue exists in the array will be
276 * checked and others will be unchecked.</li>
278 * <p>If a checkbox's name is not in the mapping at all, it will be unchecked.</p>
280 * <pre><code>var myCheckboxGroup = new Ext.form.CheckboxGroup({
293 boxLabel: 'Grouped 1'
297 boxLabel: 'Grouped 2'
301 boxLabel: 'Grouped 3'
306 myCheckboxGroup.setValue({
309 cbGroup: ['value1', 'value3']
311 * <p>The above code will cause the checkbox named 'cb1' to be checked, as well as the first and third
312 * checkboxes named 'cbGroup'. The other three checkboxes will be unchecked.</p>
313 * @param {Object} value The mapping of checkbox names to values.
314 * @return {Ext.form.CheckboxGroup} this
316 setValue: function(value) {
318 me.batchChanges(function() {
319 me.eachBox(function(cb) {
320 var name = cb.getName(),
322 if (value && name in value) {
323 if (Ext.isArray(value[name])) {
324 cbValue = Ext.Array.contains(value[name], cb.inputValue);
326 // single value, let the checkbox's own setValue handle conversion
327 cbValue = value[name];
330 cb.setValue(cbValue);
338 * <p>Returns an object containing the values of all checked checkboxes within the group. Each key-value pair
339 * in the object corresponds to a checkbox {@link Ext.form.field.Checkbox#name name}. If there is only one checked
340 * checkbox with a particular name, the value of that pair will be the String
341 * {@link Ext.form.field.Checkbox#inputValue inputValue} of that checkbox. If there are multiple checked checkboxes
342 * with that name, the value of that pair will be an Array of the selected inputValues.</p>
343 * <p>The object format returned from this method can also be passed directly to the {@link #setValue} method.</p>
344 * <p>NOTE: In Ext 3, this method returned an array of Checkbox components; this was changed to make it more
345 * consistent with other field components and with the {@link #setValue} argument signature. If you need the old
346 * behavior in Ext 4+, use the {@link #getChecked} method instead.</p>
348 getValue: function() {
350 this.eachBox(function(cb) {
351 var name = cb.getName(),
352 inputValue = cb.inputValue,
355 if (name in values) {
356 bucket = values[name];
357 if (!Ext.isArray(bucket)) {
358 bucket = values[name] = [bucket];
360 bucket.push(inputValue);
362 values[name] = inputValue;
370 * Don't return any data for submit; the form will get the info from the individual checkboxes themselves.
372 getSubmitData: function() {
377 * Don't return any data for the model; the form will get the info from the individual checkboxes themselves.
379 getModelData: function() {
383 validate: function() {
385 errors = me.getErrors(),
386 isValid = Ext.isEmpty(errors),
387 wasValid = !me.hasActiveError();
390 me.unsetActiveError();
392 me.setActiveError(errors);
394 if (isValid !== wasValid) {
395 me.fireEvent('validitychange', me, isValid);
396 me.doComponentLayout();
404 this.borrow(Ext.form.field.Base, ['markInvalid', 'clearInvalid']);