Restore support for Select2 data format in custom select control (#4712)
Credit to @jimcottrell. Refs: #4413
This commit is contained in:
parent
1b4147212d
commit
b5ed0b313a
|
|
@ -71,24 +71,68 @@ Use the `data-handler` attribute to source the select options from an AJAX handl
|
||||||
></select>
|
></select>
|
||||||
```
|
```
|
||||||
|
|
||||||
The AJAX handler should return results as an array.
|
The AJAX handler should return results in the [Select2 data format](https://select2.org/data-sources/formats).
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function onGetOptions()
|
public function onGetOptions()
|
||||||
{
|
{
|
||||||
$results = [
|
return [
|
||||||
[
|
'results' => [
|
||||||
'id' => 1,
|
[
|
||||||
'text' => 'Foobar',
|
'id' => 1,
|
||||||
],
|
'text' => 'Foo'
|
||||||
...
|
],
|
||||||
|
[
|
||||||
|
'id' => 2,
|
||||||
|
'text' => 'Bar'
|
||||||
|
]
|
||||||
|
...
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
return ['result' => $results];
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Due to the fact that JavaScript reorders numeric keys when interpreting the JSON data received by the AJAX handler, we suggest the method above for defining `results`. Support for the original `results` array format is however retained to ensure backwards compatibility.
|
Or a more full-featured example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function onGetOptions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'results' => [
|
||||||
|
[
|
||||||
|
'id' => 1,
|
||||||
|
'text' => 'Foo',
|
||||||
|
'disabled' => true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 2,
|
||||||
|
'text' => 'Bar',
|
||||||
|
'selected' => true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'text' => 'Group',
|
||||||
|
'children' => [
|
||||||
|
[
|
||||||
|
'id' => 3,
|
||||||
|
'text' => 'Child 1'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 4,
|
||||||
|
'text' => 'Child 2'
|
||||||
|
]
|
||||||
|
...
|
||||||
|
]
|
||||||
|
]
|
||||||
|
...
|
||||||
|
],
|
||||||
|
'pagination' => [
|
||||||
|
'more' => true
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The results array can be assigned to either the `result` or `results` key. As an alternative to the Select2 format, results can also be provided as an associative array (also assigned to either key). Due to the fact that JavaScript does not guarantee the order of object properties, we suggest the method above for defining results.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function onGetOptions()
|
public function onGetOptions()
|
||||||
|
|
|
||||||
|
|
@ -87,23 +87,26 @@
|
||||||
return $request
|
return $request
|
||||||
},
|
},
|
||||||
processResults: function (data, params) {
|
processResults: function (data, params) {
|
||||||
var results = data.result;
|
var results = data.result || data.results,
|
||||||
var options = [];
|
options = []
|
||||||
|
|
||||||
for (var i in results) {
|
delete(data.result)
|
||||||
if (results.hasOwnProperty(i)) {
|
if (results[0] && typeof(results[0]) === 'object') { // Pass through Select2 format
|
||||||
var isObject = i != null && i.constructor.name === 'Object';
|
options = results
|
||||||
|
}
|
||||||
options.push({
|
else { // Key-value map
|
||||||
id: isObject ? results[i].id : i,
|
for (var i in results) {
|
||||||
text: isObject ? results[i].text : results[i],
|
if (results.hasOwnProperty(i)) {
|
||||||
});
|
options.push({
|
||||||
};
|
id: i,
|
||||||
};
|
text: results[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
data.results = options
|
||||||
results: options,
|
return data
|
||||||
};
|
|
||||||
},
|
},
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3514,7 +3514,12 @@ if($element.hasClass('select-hide-selected')){extraOptions.dropdownCssClass+=' s
|
||||||
var source=$element.data('handler');if(source){extraOptions.ajax={transport:function(params,success,failure){var $request=$element.request(source,{data:params.data})
|
var source=$element.data('handler');if(source){extraOptions.ajax={transport:function(params,success,failure){var $request=$element.request(source,{data:params.data})
|
||||||
$request.done(success)
|
$request.done(success)
|
||||||
$request.fail(failure)
|
$request.fail(failure)
|
||||||
return $request},processResults:function(data,params){var results=data.result;var options=[];for(var i in results){if(results.hasOwnProperty(i)){var isObject=i!=null&&i.constructor.name==='Object';options.push({id:isObject?results[i].id:i,text:isObject?results[i].text:results[i],});};};return{results:options,};},dataType:'json'}}
|
return $request},processResults:function(data,params){var results=data.result||data.results,options=[]
|
||||||
|
delete(data.result)
|
||||||
|
if(results[0]&&typeof(results[0])==='object'){options=results}
|
||||||
|
else{for(var i in results){if(results.hasOwnProperty(i)){options.push({id:i,text:results[i],})}}}
|
||||||
|
data.results=options
|
||||||
|
return data},dataType:'json'}}
|
||||||
var separators=$element.data('token-separators')
|
var separators=$element.data('token-separators')
|
||||||
if(separators){extraOptions.tags=true
|
if(separators){extraOptions.tags=true
|
||||||
extraOptions.tokenSeparators=separators.split('|')
|
extraOptions.tokenSeparators=separators.split('|')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
import { assert } from 'chai'
|
||||||
|
import fakeDom from 'helpers/fakeDom'
|
||||||
|
|
||||||
|
describe('modules/system/assets/ui/js/select.js', function () {
|
||||||
|
describe('AJAX processResults function', function () {
|
||||||
|
let dom,
|
||||||
|
window,
|
||||||
|
processResults,
|
||||||
|
keyValResultFormat = {
|
||||||
|
value1: 'text1',
|
||||||
|
value2: 'text2'
|
||||||
|
},
|
||||||
|
select2ResultFormat = [
|
||||||
|
{
|
||||||
|
id: 'value1',
|
||||||
|
text: 'text1',
|
||||||
|
disabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'value2',
|
||||||
|
text: 'text2',
|
||||||
|
selected: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
this.timeout(1000)
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
// Load framework.js and select.js in the fake DOM
|
||||||
|
dom = fakeDom(`
|
||||||
|
<select class="custom-select" data-handler="onSearch"></select>
|
||||||
|
<script src="file://./node_modules/jquery/dist/jquery.js" id="jqueryScript"></script>
|
||||||
|
<script src="file://./modules/system/assets/js/framework.js" id="frameworkScript"></script>
|
||||||
|
<script src="file://./modules/system/assets/ui/js/select.js" id="selectScript"></script>
|
||||||
|
`)
|
||||||
|
|
||||||
|
window = dom.window
|
||||||
|
|
||||||
|
window.selectScript.onload = () => {
|
||||||
|
window.jQuery.fn.select2 = function(options) {
|
||||||
|
processResults = options.ajax.processResults
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('supports a key-value mapping on the "result" key', function () {
|
||||||
|
let result = processResults({ result: keyValResultFormat })
|
||||||
|
assert.deepEqual(result, { results: [
|
||||||
|
{
|
||||||
|
id: 'value1',
|
||||||
|
text: 'text1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'value2',
|
||||||
|
text: 'text2'
|
||||||
|
}
|
||||||
|
]})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('supports a key-value mapping on the "results" key', function() {
|
||||||
|
let result = processResults({ results: keyValResultFormat })
|
||||||
|
assert.deepEqual(result, { results: [
|
||||||
|
{
|
||||||
|
id: 'value1',
|
||||||
|
text: 'text1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'value2',
|
||||||
|
text: 'text2'
|
||||||
|
}
|
||||||
|
]})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes through other data provided with key-value mapping', function() {
|
||||||
|
let result = processResults({ result: keyValResultFormat, other1: 1, other2: '2' })
|
||||||
|
assert.include(result, { other1: 1, other2: '2'})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('supports the Select2 result format on the "result" key', function() {
|
||||||
|
let result = processResults({ result: select2ResultFormat })
|
||||||
|
assert.deepEqual(result, { results: select2ResultFormat })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes through the Select2 result format on the "results" key', function() {
|
||||||
|
let result = processResults({ results: select2ResultFormat })
|
||||||
|
assert.deepEqual(result, { results: select2ResultFormat })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes through other data provided with Select2 results format', function() {
|
||||||
|
let result = processResults({ results: select2ResultFormat, pagination: { more: true }, other: 'value' })
|
||||||
|
assert.deepInclude(result, { pagination: { more: true }, other: 'value' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes through the Select2 format with a group as the first entry', function() {
|
||||||
|
let data = [
|
||||||
|
{
|
||||||
|
text: 'Label',
|
||||||
|
children: select2ResultFormat
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
let result = processResults({ results: data })
|
||||||
|
assert.deepEqual(result, { results: data })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue