Combo
Combo (dropdown) widget. Consists of a text field with a trigger icon, which displays a List. Can be populated from a Store.
//<code-header>
fiddle.title = 'Combo';
//</code-header>
new Combo({
items : ['Fanta', 'Loranga', 'Jaffa', 'Zingo', 'Orangina'],
label : 'Soda',
labelPosition : 'above',
placeholder : 'Select a soda',
appendTo : targetElement
});Please be aware that when populating the Combo with objects or records, you have to configure valueField and displayField to point to the correct field names in your data.
This field can be used as an editor for the Column.
Basic scenarios
//<code-header>
fiddle.title = 'Basic combo';
//</code-header>
// combo with string items
new Combo({
labelPosition : 'above',
items : ['Fanta', 'Loranga', 'Jaffa', 'Zingo', 'Orangina'],
label : 'Items as strings',
appendTo : targetElement
});
// combo with object items
new Combo({
labelPosition : 'above',
items : [{ value : 'pepsi', text : 'Pepsi' }, { value : 'coke', text : 'Coca Cola' }],
label : 'Items as objects',
appendTo : targetElement
});
// non-editable combo (user can only pick from list)
new Combo({
labelPosition : 'above',
items : [{ value : 'MtnDew', text : 'Mountain Dew' }, 'Sprite', '7up'],
label : 'Not editable',
editable : false,
appendTo : targetElement
});
// editable combo (user can type to filter)
new Combo({
labelPosition : 'above',
items : ['Captain America', 'Hulk', 'She-Hulk', 'Hawkeye'],
label : 'Editable',
editable : true,
appendTo : targetElement
});Loading data from simple string array:
Filtering on typing will still work when non-editable. The characters typed are collected and used to filter the list of items in the picker. The typed value expires after 2 seconds of inactivity. This is useful when a non-editable Combo has a very large dataset and you want to filter it by typing.
The combo's configured primaryFilter is used which uses the startsWith operator
by default.
Basic scenarios
//<code-header>
fiddle.title = 'Combo';
//</code-header>
new Combo({
items : ['Fanta', 'Loranga', 'Jaffa', 'Zingo', 'Orangina'],
label : 'Soda',
labelPosition : 'above',
placeholder : 'Select a soda',
appendTo : targetElement
});Strict picker width matching
By default, a Combo's picker, auto widths around its visible content, but will be at least the width of the Combo's input area. See the first example above.
To enforce strict width matching of the picker to the input area, configure the combo's picker like this
const combo = new Combo({
items : ['Small', 'Mediunm', 'Large', 'Ridiculously huge which would cause a very wide dropdown'],
placeholder : 'Pick size of diamond for ring',
picker : {
align : {
// Override default which is 'min'
matchSize : true
}
}
});
Autocomplete / Type ahead
Implementing remote type-ahead functionality is simple, configure a store with readUrl and define
the minChars required to type before remote loading is triggered.
//<code-header>
fiddle.title = 'Remote combo';
//</code-header>
// Type-ahead combo list with remotely loaded data
new Combo({
appendTo : targetElement,
label : 'Countries',
minChars : 2,
displayField : 'name',
valueField : 'code',
filterParamName : 'filter',
store : {
readUrl : 'remotecombo'
}
});
const countries = [
{ name : 'Afghanistan', code : 'AF' },
{ name : 'Åland Islands', code : 'AX' },
{ name : 'Albania', code : 'AL' },
{ name : 'Algeria', code : 'DZ' },
{ name : 'American Samoa', code : 'AS' },
{ name : 'AndorrA', code : 'AD' },
{ name : 'Angola', code : 'AO' },
{ name : 'Anguilla', code : 'AI' },
{ name : 'Antarctica', code : 'AQ' },
{ name : 'Antigua and Barbuda', code : 'AG' },
{ name : 'Argentina', code : 'AR' },
{ name : 'Armenia', code : 'AM' },
{ name : 'Aruba', code : 'AW' },
{ name : 'Australia', code : 'AU' },
{ name : 'Austria', code : 'AT' },
{ name : 'Azerbaijan', code : 'AZ' },
{ name : 'Bahamas', code : 'BS' },
{ name : 'Bahrain', code : 'BH' },
{ name : 'Bangladesh', code : 'BD' },
{ name : 'Barbados', code : 'BB' },
{ name : 'Belarus', code : 'BY' },
{ name : 'Belgium', code : 'BE' },
{ name : 'Belize', code : 'BZ' },
{ name : 'Benin', code : 'BJ' },
{ name : 'Bermuda', code : 'BM' },
{ name : 'Bhutan', code : 'BT' },
{ name : 'Bolivia', code : 'BO' },
{ name : 'Bosnia and Herzegovina', code : 'BA' },
{ name : 'Botswana', code : 'BW' },
{ name : 'Bouvet Island', code : 'BV' },
{ name : 'Brazil', code : 'BR' },
{ name : 'British Indian Ocean Territory', code : 'IO' },
{ name : 'Brunei Darussalam', code : 'BN' },
{ name : 'Bulgaria', code : 'BG' },
{ name : 'Burkina Faso', code : 'BF' },
{ name : 'Burundi', code : 'BI' },
{ name : 'Cambodia', code : 'KH' },
{ name : 'Cameroon', code : 'CM' },
{ name : 'Canada', code : 'CA' },
{ name : 'Cape Verde', code : 'CV' },
{ name : 'Cayman Islands', code : 'KY' },
{ name : 'Central African Republic', code : 'CF' },
{ name : 'Chad', code : 'TD' },
{ name : 'Chile', code : 'CL' },
{ name : 'China', code : 'CN' },
{ name : 'Christmas Island', code : 'CX' },
{ name : 'Cocos (Keeling) Islands', code : 'CC' },
{ name : 'Colombia', code : 'CO' },
{ name : 'Comoros', code : 'KM' },
{ name : 'Congo', code : 'CG' },
{ name : 'Congo, The Democratic Republic of the', code : 'CD' },
{ name : 'Cook Islands', code : 'CK' },
{ name : 'Costa Rica', code : 'CR' },
{ name : 'Cote D\'Ivoire', code : 'CI' },
{ name : 'Croatia', code : 'HR' },
{ name : 'Cuba', code : 'CU' },
{ name : 'Cyprus', code : 'CY' },
{ name : 'Czech Republic', code : 'CZ' },
{ name : 'Denmark', code : 'DK' },
{ name : 'Djibouti', code : 'DJ' },
{ name : 'Dominica', code : 'DM' },
{ name : 'Dominican Republic', code : 'DO' },
{ name : 'Ecuador', code : 'EC' },
{ name : 'Egypt', code : 'EG' },
{ name : 'El Salvador', code : 'SV' },
{ name : 'Equatorial Guinea', code : 'GQ' },
{ name : 'Eritrea', code : 'ER' },
{ name : 'Estonia', code : 'EE' },
{ name : 'Ethiopia', code : 'ET' },
{ name : 'Falkland Islands (Malvinas)', code : 'FK' },
{ name : 'Faroe Islands', code : 'FO' },
{ name : 'Fiji', code : 'FJ' },
{ name : 'Finland', code : 'FI' },
{ name : 'France', code : 'FR' },
{ name : 'French Guiana', code : 'GF' },
{ name : 'French Polynesia', code : 'PF' },
{ name : 'French Southern Territories', code : 'TF' },
{ name : 'Gabon', code : 'GA' },
{ name : 'Gambia', code : 'GM' },
{ name : 'Georgia', code : 'GE' },
{ name : 'Germany', code : 'DE' },
{ name : 'Ghana', code : 'GH' },
{ name : 'Gibraltar', code : 'GI' },
{ name : 'Greece', code : 'GR' },
{ name : 'Greenland', code : 'GL' },
{ name : 'Grenada', code : 'GD' },
{ name : 'Guadeloupe', code : 'GP' },
{ name : 'Guam', code : 'GU' },
{ name : 'Guatemala', code : 'GT' },
{ name : 'Guernsey', code : 'GG' },
{ name : 'Guinea', code : 'GN' },
{ name : 'Guinea-Bissau', code : 'GW' },
{ name : 'Guyana', code : 'GY' },
{ name : 'Haiti', code : 'HT' },
{ name : 'Heard Island and Mcdonald Islands', code : 'HM' },
{ name : 'Holy See (Vatican City State)', code : 'VA' },
{ name : 'Honduras', code : 'HN' },
{ name : 'Hong Kong', code : 'HK' },
{ name : 'Hungary', code : 'HU' },
{ name : 'Iceland', code : 'IS' },
{ name : 'India', code : 'IN' },
{ name : 'Indonesia', code : 'ID' },
{ name : 'Iran, Islamic Republic Of', code : 'IR' },
{ name : 'Iraq', code : 'IQ' },
{ name : 'Ireland', code : 'IE' },
{ name : 'Isle of Man', code : 'IM' },
{ name : 'Israel', code : 'IL' },
{ name : 'Italy', code : 'IT' },
{ name : 'Jamaica', code : 'JM' },
{ name : 'Japan', code : 'JP' },
{ name : 'Jersey', code : 'JE' },
{ name : 'Jordan', code : 'JO' },
{ name : 'Kazakhstan', code : 'KZ' },
{ name : 'Kenya', code : 'KE' },
{ name : 'Kiribati', code : 'KI' },
{ name : 'Korea, Democratic People\'S Republic of', code : 'KP' },
{ name : 'Korea, Republic of', code : 'KR' },
{ name : 'Kuwait', code : 'KW' },
{ name : 'Kyrgyzstan', code : 'KG' },
{ name : 'Lao People\'S Democratic Republic', code : 'LA' },
{ name : 'Latvia', code : 'LV' },
{ name : 'Lebanon', code : 'LB' },
{ name : 'Lesotho', code : 'LS' },
{ name : 'Liberia', code : 'LR' },
{ name : 'Libyan Arab Jamahiriya', code : 'LY' },
{ name : 'Liechtenstein', code : 'LI' },
{ name : 'Lithuania', code : 'LT' },
{ name : 'Luxembourg', code : 'LU' },
{ name : 'Macao', code : 'MO' },
{ name : 'Macedonia, The Former Yugoslav Republic of', code : 'MK' },
{ name : 'Madagascar', code : 'MG' },
{ name : 'Malawi', code : 'MW' },
{ name : 'Malaysia', code : 'MY' },
{ name : 'Maldives', code : 'MV' },
{ name : 'Mali', code : 'ML' },
{ name : 'Malta', code : 'MT' },
{ name : 'Marshall Islands', code : 'MH' },
{ name : 'Martinique', code : 'MQ' },
{ name : 'Mauritania', code : 'MR' },
{ name : 'Mauritius', code : 'MU' },
{ name : 'Mayotte', code : 'YT' },
{ name : 'Mexico', code : 'MX' },
{ name : 'Micronesia, Federated States of', code : 'FM' },
{ name : 'Moldova, Republic of', code : 'MD' },
{ name : 'Monaco', code : 'MC' },
{ name : 'Mongolia', code : 'MN' },
{ name : 'Montserrat', code : 'MS' },
{ name : 'Morocco', code : 'MA' },
{ name : 'Mozambique', code : 'MZ' },
{ name : 'Myanmar', code : 'MM' },
{ name : 'Namibia', code : 'NA' },
{ name : 'Nauru', code : 'NR' },
{ name : 'Nepal', code : 'NP' },
{ name : 'Netherlands', code : 'NL' },
{ name : 'Netherlands Antilles', code : 'AN' },
{ name : 'New Caledonia', code : 'NC' },
{ name : 'New Zealand', code : 'NZ' },
{ name : 'Nicaragua', code : 'NI' },
{ name : 'Niger', code : 'NE' },
{ name : 'Nigeria', code : 'NG' },
{ name : 'Niue', code : 'NU' },
{ name : 'Norfolk Island', code : 'NF' },
{ name : 'Northern Mariana Islands', code : 'MP' },
{ name : 'Norway', code : 'NO' },
{ name : 'Oman', code : 'OM' },
{ name : 'Pakistan', code : 'PK' },
{ name : 'Palau', code : 'PW' },
{ name : 'Palestinian Territory, Occupied', code : 'PS' },
{ name : 'Panama', code : 'PA' },
{ name : 'Papua New Guinea', code : 'PG' },
{ name : 'Paraguay', code : 'PY' },
{ name : 'Peru', code : 'PE' },
{ name : 'Philippines', code : 'PH' },
{ name : 'Pitcairn', code : 'PN' },
{ name : 'Poland', code : 'PL' },
{ name : 'Portugal', code : 'PT' },
{ name : 'Puerto Rico', code : 'PR' },
{ name : 'Qatar', code : 'QA' },
{ name : 'Reunion', code : 'RE' },
{ name : 'Romania', code : 'RO' },
{ name : 'Russian Federation', code : 'RU' },
{ name : 'RWANDA', code : 'RW' },
{ name : 'Saint Helena', code : 'SH' },
{ name : 'Saint Kitts and Nevis', code : 'KN' },
{ name : 'Saint Lucia', code : 'LC' },
{ name : 'Saint Pierre and Miquelon', code : 'PM' },
{ name : 'Saint Vincent and the Grenadines', code : 'VC' },
{ name : 'Samoa', code : 'WS' },
{ name : 'San Marino', code : 'SM' },
{ name : 'Sao Tome and Principe', code : 'ST' },
{ name : 'Saudi Arabia', code : 'SA' },
{ name : 'Senegal', code : 'SN' },
{ name : 'Serbia and Montenegro', code : 'CS' },
{ name : 'Seychelles', code : 'SC' },
{ name : 'Sierra Leone', code : 'SL' },
{ name : 'Singapore', code : 'SG' },
{ name : 'Slovakia', code : 'SK' },
{ name : 'Slovenia', code : 'SI' },
{ name : 'Solomon Islands', code : 'SB' },
{ name : 'Somalia', code : 'SO' },
{ name : 'South Africa', code : 'ZA' },
{ name : 'South Georgia and the South Sandwich Islands', code : 'GS' },
{ name : 'Spain', code : 'ES' },
{ name : 'Sri Lanka', code : 'LK' },
{ name : 'Sudan', code : 'SD' },
{ name : 'Suriname', code : 'SR' },
{ name : 'Svalbard and Jan Mayen', code : 'SJ' },
{ name : 'Swaziland', code : 'SZ' },
{ name : 'Sweden', code : 'SE' },
{ name : 'Switzerland', code : 'CH' },
{ name : 'Syrian Arab Republic', code : 'SY' },
{ name : 'Taiwan, Province of China', code : 'TW' },
{ name : 'Tajikistan', code : 'TJ' },
{ name : 'Tanzania, United Republic of', code : 'TZ' },
{ name : 'Thailand', code : 'TH' },
{ name : 'Timor-Leste', code : 'TL' },
{ name : 'Togo', code : 'TG' },
{ name : 'Tokelau', code : 'TK' },
{ name : 'Tonga', code : 'TO' },
{ name : 'Trinidad and Tobago', code : 'TT' },
{ name : 'Tunisia', code : 'TN' },
{ name : 'Turkey', code : 'TR' },
{ name : 'Turkmenistan', code : 'TM' },
{ name : 'Turks and Caicos Islands', code : 'TC' },
{ name : 'Tuvalu', code : 'TV' },
{ name : 'Uganda', code : 'UG' },
{ name : 'Ukraine', code : 'UA' },
{ name : 'United Arab Emirates', code : 'AE' },
{ name : 'United Kingdom', code : 'GB' },
{ name : 'United States', code : 'US' },
{ name : 'United States Minor Outlying Islands', code : 'UM' },
{ name : 'Uruguay', code : 'UY' },
{ name : 'Uzbekistan', code : 'UZ' },
{ name : 'Vanuatu', code : 'VU' },
{ name : 'Venezuela', code : 'VE' },
{ name : 'Viet Nam', code : 'VN' },
{ name : 'Virgin Islands, British', code : 'VG' },
{ name : 'Virgin Islands, U.S.', code : 'VI' },
{ name : 'Wallis and Futuna', code : 'WF' },
{ name : 'Western Sahara', code : 'EH' },
{ name : 'Yemen', code : 'YE' },
{ name : 'Zambia', code : 'ZM' },
{ name : 'Zimbabwe', code : 'ZW' }
];
// Mock server endpoint for demo purposes
AjaxHelper.mockUrl('remotecombo', (url, params) => {
const matches = params ?
countries.filter(country => StringHelper.stripDiacritics(country.name.toLowerCase()).startsWith(StringHelper.stripDiacritics(params.filter.toLowerCase()))) :
countries;
return {
responseText : JSON.stringify({
success : true,
total : matches.length,
data : matches
})
};
});Snippet: Loading data from simple string array
const combo = new Combo({
items : ['Small', 'Smaller', 'Really small', 'Tiny'],
placeholder : 'Pick size of diamond for ring'
});
Loading data from array with item configs:
const combo = new Combo({
items : [{ value: 'a', text: 'First' }, { value: 'z', text: 'Last' }]
});
Loading data from store:
const combo = new Combo({
store : memberStore,
valueField : 'id',
displayField : 'name'
});
Editability
When configured with editable : false, the field may still be operated and have a value selected
from the picker.
This setting just means that the textual input field may not be edited by the UI. In this mode, the picker's displayed dataset my still be filtered by typing while the picker is visible.
The combo's configured primaryFilter is used which uses the startsWith operator
by default.
Grouping the list
To group the list contents, simply group your store using groupers. You can decide if clicking a header should toggle all group children (or if it should do nothing) with the allowGroupSelect flag.
const combo = new Combo({
width : 400,
displayField : 'name',
valueField : 'id',
multiSelect : true,
picker : {
allowGroupSelect : false,
// Show icon based on group name
groupHeaderTpl : (record, groupName) => `
<i class="icon-${groupName}"></i>${groupName}
`
},
value : [1, 4],
store : new Store({
fields : [
'type'
],
groupers : [
{ field : 'type', ascending : true }
],
data : [
{ id : 1, name : 'pizza', type : 'food' },
{ id : 2, name : 'bacon', type : 'food' },
{ id : 3, name : 'egg', type : 'food' },
{ id : 4, name : 'Gin tonic', type : 'drinks' },
{ id : 5, name : 'Wine', type : 'drinks' },
{ id : 6, name : 'Beer', type : 'drinks' }
]
})
});
This demo uses multi-selection and a grouped list:
//<code-header>
fiddle.title = 'Multiselect combo';
CSSHelper.insertRule('.foodmenu-combo .b-chip i { margin-inline-end:0.4em }', targetElement.getRootNode());
CSSHelper.insertRule('.b-list-item-group-header { font-size:1.1em; }', targetElement.getRootNode());
CSSHelper.insertRule('.b-foodmenu-item { display:flex; flex-direction:column; align-items:flex-start; margin-inline-start:0.3em}', targetElement.getRootNode());
CSSHelper.insertRule('.b-foodmenu-item span.name { font-size:1.1em; }', targetElement.getRootNode());
CSSHelper.insertRule('.b-foodmenu-item small { display:block; color:#999; margin-top:0.3em;}', targetElement.getRootNode());
CSSHelper.insertRule('.b-foodmenu-item small { display:block; color:#999; margin-top:0.3em;}', targetElement.getRootNode());
//</code-header>
// multiSelect
new Combo({
appendTo : targetElement,
multiSelect : true,
label : 'Skills - multiSelect',
value : ['Javascript', 'SQL'],
items : [
'BASIC',
'COBOL',
'FORTRAN',
'DBL',
'SQL',
'C/C++',
'Java',
'Javascript'
],
width : '100%'
});
new Combo({
appendTo : targetElement,
width : '100%',
multiSelect : true,
cls : 'foodmenu-combo',
style : 'margin-top:3em',
label : 'Grouped list + multiSelect. Type to filter the picker when open',
displayField : 'name',
valueField : 'id',
value : [1, 5, 9],
listCls : 'grouped-combo',
picker : {
groupHeaderTpl : (record, groupName) => `
<i class="fa fa-${groupName === 'Food' ? 'pizza-slice' : groupName === 'Drinks' ? 'wine-glass' : 'utensils'}" style="margin-inline-end:0.8em;"></i>${groupName}
`
},
chipView : {
iconTpl : (record) => `<i class="fa fa-${record.icon}"></i>`
},
listItemTpl : record => `
<div class="b-foodmenu-item">
<span class="name">${record.name}</span>
<small>${record.calories} calories</small>
</div>
`,
editable : false,
store : {
fields : [
'type',
'calories',
'icon'
],
groupers : [
{ field : 'type', ascending : false }
],
data : [
{ id : 1, name : 'Cheese sticks', type : 'starters', calories : 312, icon : 'cheese' },
{ id : 2, name : 'Fried onion rings', type : 'starters', calories : 234, icon : 'ring' },
{ id : 3, name : 'Hummus', type : 'starters', calories : 532, icon : 'seedling' },
{ id : 4, name : 'Fried fish', type : 'food', calories : 243, icon : 'fish' },
{ id : 5, name : 'Pizza', type : 'food', calories : 687, icon : 'pizza-slice' },
{ id : 6, name : 'Hamburger', type : 'food', calories : 734, icon : 'hamburger' },
{ id : 7, name : 'Hot dog', type : 'food', calories : 435, icon : 'hotdog' },
{ id : 8, name : 'Salad', type : 'food', calories : 112, icon : 'salad' },
{ id : 9, name : 'Gin tonic', type : 'drinks', calories : 145, icon : 'glass-martini' },
{ id : 10, name : 'Wine', type : 'drinks', calories : 150, icon : 'glass-wine' },
{ id : 11, name : 'Soda', type : 'drinks', calories : 205, icon : 'glass-citrus' },
{ id : 12, name : 'Beer', type : 'drinks', calories : 109, icon : 'beer' }
]
}
});Shared Stores
More than one Combo may share a Store if they are required to draw their values from a shared data set.
The only limitation here is that the characteristics of the filter that is applied to the store by typing are inherited from the first combo. So for example, all would be caseSensitive or all case-insensitve, and all would use the same filterOperator.
In the example below, all three email address inputs use the same store of recipients.
//<code-header>
fiddle.title = 'Email multiselect';
//</code-header>
const
emailAddresses = new Store({
data : [
{ id : 10, name : 'Mike McGregor', email : '[email protected]' },
{ id : 11, name : 'Linda Ewans', email : '[email protected]' },
{ id : 12, name : 'Don Scott', email : '[email protected]' },
{ id : 13, name : 'Karen Smith', email : '[email protected]' },
{ id : 14, name : 'Doug Johnson', email : '[email protected]' },
{ id : 15, name : 'Jenny Adams', email : '[email protected]' },
{ id : 16, name : 'Daniel Williams', email : '[email protected]' },
{ id : 17, name : 'Melissa Brown', email : '[email protected]' },
{ id : 18, name : 'John Jones', email : '[email protected]' },
{ id : 19, name : 'Jane Miller', email : '[email protected]' },
{ id : 20, name : 'Theo Davis', email : '[email protected]' },
{ id : 21, name : 'Lisa More', email : '[email protected]' },
{ id : 22, name : 'Adam Wilson', email : '[email protected]' },
{ id : 23, name : 'Mary Taylor', email : '[email protected]' },
{ id : 24, name : 'Barbara Anderson', email : '[email protected]' },
{ id : 25, name : 'James Thomas', email : '[email protected]' },
{ id : 26, name : 'David Jackson', email : '[email protected]' }
]
}),
comboConfig = {
type : 'combo',
label : 'To',
multiSelect : true,
store : emailAddresses,
displayField : 'name',
chipView : {
tooltip : {
cls : 'b-recipient-card',
newInstance : true,
forSelector : '.b-chip',
getHtml : 'up.emailChipTooltip'
}
}
};
new Panel({
appendTo : targetElement,
title : 'Compose',
height : 500,
labelPosition : 'align-before',
items : {
// Three almost identical Combos
to : { ...comboConfig, required : true },
cc : { ...comboConfig, label : 'CC' },
bcc : { ...comboConfig, label : 'BCC' },
// A text field for the subject
subject : {
type : 'textfield',
label : 'Subject',
onInput : 'up.onSubjectInput'
},
// And the body textaera
body : {
type : 'textarea',
label : 'Message',
height : 100,
required : true
}
},
bbar : {
items : {
send : {
text : 'Send',
onClick : 'up.onSendClick'
}
}
},
onSendClick() {
this.items.forEach(i => i.syncInvalid());
if (!this.items.every(i => i.isValid)) {
Toast.show('Please complete the mandatory fields');
}
else {
MessageDialog.alert({
title : 'Email system',
message : 'Sent'
});
}
},
onSubjectInput({ value }) {
this.title = value || 'Subject';
},
emailChipTooltip({ tip, activeTarget }) {
const person = tip.owner.getRecordFromElement(activeTarget);
return {
children : [{
className : 'b-recipient-card-name',
text : person.name
}, {
className : 'b-recipient-card-email',
text : person.email
}]
};
}
});This may be operated using the keyboard. ArrowDown opens the picker, and then ArrowDown and
ArrowUp navigate the picker's options. Enter selects an active option in the picker. Escape
closes the picker.
Configs
145
Configs
145Other
Enable caching of the last retrieved result until the timeout is reached.
Configure this either with:
- a number of minutes,
- a Duration object,
- a DurationConfig object, or
- a string parseable by parseDuration, e.g.
10m,2 hours, etc.
This prevents unnecessary request roundtrips to the backend when reopening the picker and the filter has not been changed. The cache expiry timer is restarted on each outgoing request.
Falsy or unparseable values or negative numbers mean no caching.
Configure as true to force case matching when filtering the dropdown list based upon the typed value.
A config object to configure the ChipView to display the
selected value set when multiSelect is true.
For example the itemTpl or iconTpl might be configured to display richer chips for selected items.
true to clear value typed to a multiselect combo when picker is collapsed
Specify false to not clear value typed to a multiselect combo when an item is selected.
Set to true to clear this field when user empties the input element
If configured as true, this means that when an unmatched string is typed into the
combo's input field, and ENTER, or the multiValueSeparator is typed,
a new record will be created using the typed string as the displayField.
If configured as a function, or the name of a function in the owning component hierarchy, the function will be called passing the string and combo field instance and should return the record to add (if any).
The new record will be appended to the store, and the value selected.
If the Store is an AjaxStore, the new record will be eiligible for syncing to the database through its createUrl.
If the AjaxStore is configured to autoCommit,
the record will be synced immediately. If the server does not accept the new addition,
the field is placed temporarily into an invalid state with a message that explains this.
For example:
new Combo({
label : 'Employee name',
store : employees,
createOnUnmatched(name, combo) {
name = validateEmployeeName(name);
if (name) {
return new Employee({
name,
email : generateEmployeeEmail(name)
});
}
else {
combo.setError('Invalid new employee name');
}
}
});
| Parameter | Type | Description |
|---|---|---|
name | String | Record name |
combo | Combo | Combo instance |
New record
Field used for item text when populating from store
Template function that can be used to customize the displayed value
| Parameter | Type | Description |
|---|---|---|
record | Model | The record to provide a textual value for |
combo | Combo | A reference to this Combo |
Text to display in the drop down when there are no items in the underlying store.
Defaults to use the noResults localization string ("No results" in En locale).
A function which creates an array of values for the filterParamName to pass any filters to the server upon load.
The default behaviour is just to set the parameter value to the filter's value,
but the filter can be fully encoded for example:
{
encodeFilterParams(filters) {
const result = [];
for (const { property, operator, value, caseSensitive } of filters) {
result.push(JSON.stringify({
field : property,
operator,
value,
caseSensitive
}));
}
return result;
}
| Parameter | Type | Description |
|---|---|---|
filters | Object[] | filters |
array of values
If false, filtering will be triggered once you exceed minChars. To filter only when
hitting Enter key, set this to true;
The name of an operator type as implemented in operator to use when filtering the dropdown list based upon the typed value.
This defaults to 'startsWith', but the '*' operator may be used to match all
values which contain the typed value.
Not used when filterParamName is set to cause remote dropdown loading. The exact filtering operation is up to the server.
If the dropdown is to be populated with a filtered query to a remote server, specify the name of the parameter to pass the typed string here. By default, the string is simply sent as the value of the parameter. For special encoding, configure the combo with encodeFilterParams
When multiSelect is true, you may configure filterSelected as
true to hide items in the dropdown when they are added to the selection.
It will appear as if the requested item has "moved" into the field's
ChipView.
By default, the picker is hidden on selection in single select mode, and
remains to allow more selections when multiSelect is true.
Setting this to a Boolean value can override that default.
Configure as true to hide the expand trigger. This is automatically set to true if
remote filtering is enabled by setting the filterParamName config.
Configure this as true to render the dropdown list as a permanently visible list
in the document flow immediately below the input area instead of as a popup.
This also hides the expand trigger since it is not needed.
Rows to display in the dropdown (list items).
If an object, the property names provide the value for the Combo, and the property values provide the displayed test in the list and input area eg:
items : {
small : 'Small',
medium : 'Medium',
large : 'Large'
}
If an array, each entry may be
- an object containing properties which must include the valueField and displayField which populates the dropdown with text and provides the corresponding field value.
- An array whose first value provides the value for the Combo and whose second value provides the displayed test in the list and input area.
- An array of values where the valueField and displayField are the same.
eg:
items : [
{value : 'small', text : 'Small'},
{value : 'medium', text : 'Medium'},
{value : 'large', text : 'Large'},
]
or
items : [
['small', 'Small'],
['medium', 'Medium'],
['large', 'Large'],
]
or
items : [ 'Small', 'Medium', 'Large' ]
The delay in milliseconds to wait after the last keystroke before filtering the list.
This is a minimum of 300ms for remote filtering to keep network requests manageable, and defaults to 10ms for locally filtered stores.
CSS class to add to picker
Template string used to render the list items in the dropdown list
new Combo({
listItemTpl : ({ text }) => `<div class="combo-color-box ${text}"></div>${text}`,
editable : false,
items : [
'Black',
'Green',
'Orange',
'Pink',
'Purple',
'Red',
'Teal'
]
});
| Parameter | Type | Description |
|---|---|---|
record | Model | The record representing the item being rendered |
The minimum string length to trigger the filtering, only relevant when editable is true.
This defaults to 1 in the case of local filtering, but 4 if the
filterParamName is set to cause remote dropdown loading.
Set to true to allow selection of multiple values from the dropdown list.
Each value is displayed as a "Chip" to the left of the input area. Chips may be
selected using the LEFT and RIGHT arrow keys and deleted using the DELETE key
to remove values from the field. There is also a clickable close icon in each chip.
Use toggleAllIfCtrlPressed to implement "select all" behaviour.
{
type : 'combo',
store : myStore,
picker : {
toggleAllIfCtrlPressed : true
}
}
A key value which, when typed in a multiSelect Combo, selects the currently active item in the picker, and clears the input field ready for another match to be typed.
This implies that the picker will display an anchor pointer, but also means that the picker will align closer to the input field so that the pointer pierces the pickerAlignElement
Width of picker, defaults to this combo's pickerAlignElement width
Optionally a Filter config object which the combo should use for
filtering using the typed value.
This may use a filterBy property to test its value against any field in the passed record.
{
type : 'combo',
store : myStore,
primaryFilter : {
filterBy(record) {
if (this.value == null) {
return true;
}
const value = this.value.toLowerCase();
// Match typed value with forename or surname
return record.forename.toLowerCase().startsWith(value)
|| record.surname.toLowerCase().startsWith(value);
}
}
}
Store used to populate items. Also accepts a Store config object
How to query the store upon click of the expand trigger. Specify one of these values:
'all'- Clear the filter and display the whole dataset in the dropdown.'last'- Filter the dataset using the last filter value.null/any other - Use the value in the input field to filter the dataset.
true to cause the field to be in an invalid state while the typed filter string does not match a record in the store.
The initial value of this Combo box. In single select mode (default) it's a simple string value, for multiSelect mode, it should be an array of record ids.
Field used for item value when populating from store. Setting this to null will
yield the selected record as the Combo's value.
Leaving this value undefined means using the idField of the
modelClass of the store
as the value field.
DOM
Float & align
Input element
Label
Layout
Misc
Scrolling
Properties
96
Properties
96Class hierarchy
Other
The name of an operator type as implemented in operator to use when filtering the dropdown list based upon the typed value.
This defaults to 'startsWith', but the '*' operator may be used to match all
values which contain the typed value.
Not used when filterParamName is set to cause remote dropdown loading. The exact filtering operation is up to the server.
Returns array of the Combo's inner HTML elements. This getter can be overriden.
Returns true if this field has no selected records.
Set to true to allow selection of multiple values from the dropdown list.
Each value is displayed as a "Chip" to the left of the input area. Chips may be
selected using the LEFT and RIGHT arrow keys and deleted using the DELETE key
to remove values from the field. There is also a clickable close icon in each chip.
Use toggleAllIfCtrlPressed to implement "select all" behaviour.
{
type : 'combo',
store : myStore,
picker : {
toggleAllIfCtrlPressed : true
}
}
A constant value for the triggerAction config to indicate that clicking the trigger should filter the dataset using the last filter query string, not the input field value.
Store used to populate items. Also accepts a Store config object
Get/sets combo value, selects corresponding item in the list Setting null clears the field.
If multiSelect is true, then multiple values may be passed as an array.
If the values are records, these become the selected record set held by valueCollection,
and the value yielded by this field is an array of all the valueFields from the records.
A Collection which holds the currently selected records from the store which dictates this field's value.
Usually, this will contain one record, the record selected.
When multiSelect is true, there may be several records selected.
CSS
DOM
Layout
Misc
Functions
67
Functions
67Configuration
Events
Misc
Other
Widget hierarchy
Events
20
Events
20The default action was performed (an item in the list was selected)
// Adding a listener using the "on" method
combo.on('action', ({ source, value, record, records, userAction }) => {
});| Parameter | Type | Description |
|---|---|---|
source | Combo | The combo |
value | * | The value of the selected record |
record | Model | Selected record |
records | Model[] | Selected records as an array if multiSelect is |
userAction | Boolean |
|
User typed into the field. Please note that the value attached to this event is the raw input field value and not the combos value
// Adding a listener using the "on" method
combo.on('input', ({ source, value, event }) => {
});| Parameter | Type | Description |
|---|---|---|
source | Combo | The combo. |
value | String | Raw input value |
event | Event | The triggering DOM event if any. |
An item in the list was selected
// Adding a listener using the "on" method
combo.on('select', ({ source, record, records, userAction }) => {
});| Parameter | Type | Description |
|---|---|---|
source | Combo | The combo |
record | Model | Selected record |
records | Model[] | Selected records as an array if multiSelect is |
userAction | Boolean |
|
Event handlers
20
Event handlers
20The default action was performed (an item in the list was selected)
new Combo({
onAction({ source, value, record, records, userAction }) {
}
});| Parameter | Type | Description |
|---|---|---|
source | Combo | The combo |
value | * | The value of the selected record |
record | Model | Selected record |
records | Model[] | Selected records as an array if multiSelect is |
userAction | Boolean |
|
User typed into the field. Please note that the value attached to this event is the raw input field value and not the combos value
new Combo({
onInput({ source, value, event }) {
}
});| Parameter | Type | Description |
|---|---|---|
source | Combo | The combo. |
value | String | Raw input value |
event | Event | The triggering DOM event if any. |
An item in the list was selected
new Combo({
onSelect({ source, record, records, userAction }) {
}
});| Parameter | Type | Description |
|---|---|---|
source | Combo | The combo |
record | Model | Selected record |
records | Model[] | Selected records as an array if multiSelect is |
userAction | Boolean |
|
Typedefs
7
Typedefs
7CSS variables
132
CSS variables
132| Name | Description |
|---|---|
--b-combo-filled-chip-view-padding-top | FilledTop padding for chipview in combo when using "filled" rendition |
--b-combo-filled-label-before-chip-view-padding-top | FilledTop padding for chipview in combo with label displayed before and using "filled" rendition |
--b-combo-outlined-chip-view-padding-top | OutlinedTop padding for chipview in combo when using "outlined" rendition |
--b-combo-outlined-label-before-chip-view-padding-top | OutlinedTop padding for chipview in combo with label displayed before and using "outlined" rendition |
--b-combo-chip-font-size | Font size for chipview in combo |
--b-combo-chip-view-margin-block | Block margin for chipview in combo |
--b-combo-chip-view-padding | Padding for chipview in combo |
--b-combo-chip-view-min-height | Minimum height for chipview in combo (to avoid it collapsing when empty) |