I have implemented 2 custom functions with select2
:
- Select parent node, add parent node to selected and disable child nodes.
- Unselect parent node, remove from selected and enable child nodes.
This is working, except when adding and removing parent node in this order:
- Select parent
- Open emerging options (you can see child nodes are disabled)
- Click outside search box, so emerging options close
- Unselect parent
- Open emerging box again (child nodes are still disabled)
On the contrary, it works if step 2 and 3 are not done.
- Select parent
- Unselect parent
- Open emerging options (you can see child nodes are enabled back)
Please see a working example:
const tags = [{
'id': 1,
'text': 'Parent 1',
'children': [{
'id': 'tag11',
'text': 'Tag 11'
},
{
'id': 'tag12',
'text': 'Tag 12'
}, {
'id': 'parent1',
'text': 'Parent 1'
},
],
},
{
'id': 2,
'text': 'Parent 2',
'children': [{
'id': 'tag21',
'text': 'Tag 21'
},
{
'id': 'tag22',
'text': 'Tag 22'
},
{
'id': 'parent2',
'text': 'Parent 2'
},
],
}
];
$(document).ready(function() {
const selectField = $('#target');
selectField.select2({
width: '300px',
templateResult: function(option) {
if (option.element && (option.element).hasAttribute('hidden')) {
return null;
}
return option.text;
}
});
selectField.on('select2:open', function(e) {
let allOptionsStart = this.options;
$('#select2-target-results').on('click', function(event) {
let allOptions = [];
$.each(allOptionsStart, (key, option) => {
allOptions[option.value] = option;
});
const data = $(event.target).html();
const selectedOptionGroup = data.toString().trim();
let selectedOptionGroupId = '';
$.each(tags, (key, tag) => {
if (selectedOptionGroup.toString() === tag.text.toString()) {
$.each(tag.children, (key, child) => {
if (selectedOptionGroup.toString() === child.text) {
selectedOptionGroupId = child.id;
}
const jTag = $(allOptions[child.id]);
if (Object.keys(jTag).length > 0) {
if (!jTag[0].hidden) {
jTag[0].disabled = true;
}
}
});
}
});
$('select').select2({
width: '300px',
templateResult: function(option) {
if (option.element && (option.element).hasAttribute('hidden')) {
return null;
}
return option.text;
}
});
event.stopPropagation();
let options = selectField.val();
if (options === null || options === '') {
options = [];
}
options.push(selectedOptionGroupId);
selectField.val(options);
selectField.trigger('change'); // Notify any JS components that the value changed
selectField.select2('close');
});
});
selectField.on('select2:unselecting', function(e) {
let allOptions = [];
$.each(this.options, (key, option) => {
allOptions[option.value] = option;
});
const unselectedGroupText = e.params.args.data.text;
$.each(tags, (key, tag) => {
if (unselectedGroupText === tag.text) {
$.each(tag.children, (key, childTag) => {
const jTag = $(allOptions[childTag.id]);
if (Object.keys(jTag).length > 0) {
if (!jTag[0].hidden) {
jTag[0].disabled = false;
}
}
});
}
});
});
});
li.select2-results__option strong.select2-results__group:hover {
background-color: #ddd;
cursor: pointer;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<select id="target" data-placeholder="Select an option" multiple='multiple'>
<optgroup label="Parent 1">
<option value="tag11">Tag 11</option>
<option value="tag12">Tag 12</option>
<option value="parent1" hidden>Parent 1</option>
</optgroup>
<optgroup label="Parent 2">
<option class="tag21">Tag 21</option>
<option class="tag22">Tag 22</option>
<option value="parent2" hidden>Parent 2</option>
</optgroup>
</select>
jsfiddle (just in case): https://jsfiddle.net/ivantxo/bzu2xmp9/151/
Can anyone please help me to see what’s wrong?
This should work in both cases.
Thanks.
2
Answers
I have figured out this much. I hope it will be helpful.
One solution would be to add some classes to the options, to make identification easier.
I placed the select2 plugin assignment in a function, to facilitate reuse, as in this implementation, the select2 destroy method will be used to reapply the plugin and update the display.
I used click monitoring in the optgroups in a delegated way, for practical reasons. When the click event occurs, it compares the label of the select2 component with the label of the
optgroup
, to disable and remove the selection of child options with the optnormal class, in addition to selecting theoption
with the opthidden class.selectField.on('select2:unselecting', function(e) {
allows you to find out which element was removed from the selection withe.params.args.data.element
, simply checking if it is the option with the opthidden class to fetch the parentoptgroup
and re-enable the child options with the optnormal class.