Choices
Choices copied to clipboard
Allow user-created choices for selects
Description
- Added a new configuration option called
addChoices
which affects selects. It is false by default to maintain old behavior. - When
addChoices
is set to true, the user can type an option into the field and press enter, and it will be added as a new option and selected.
Motivation and Context
This addresses #39 which is allowing users to add choices to selects.
How Has This Been Tested?
It works on the project I'm working on. The existing tests and new tests that I added pass.
Screenshots (if appropriate):
Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
Checklist:
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
I have rebased with master to fix merge conflicts since I opened this PR, added tests, added a gif demo, and updated the readme. Please let me know if there's anything else I can address so this can be considered for merging. ✧・゚:*╰(◕‿◕。╰)
Is there any chance that this feature could be added?
Sorry, I've been really busy recently. Will get this reviewed and merged ASAP
Hi, just wondering if/when this will be reviewed and merged? Very keen on using this feature :)
@caramiki @jshjohnson guys any news about this amazing feature? thanks
Is there anything we can do to help with getting this feature added?
Please add this. I had to develop it on my own.
Ended up with a makeshift workaround code.
const choicesIssue39Fix = (choicesElement) => {
choicesElement.input.element.addEventListener('keydown', (keypressEvent) => {
if (keypressEvent.keyCode === 13 && keypressEvent.target.value) {
let keywordHits = 0;
[...choicesElement.choiceList.element.children].forEach((element) => {
if (element.innerText.includes(keypressEvent.target.value)) {
keywordHits += 1;
}
});
if (!keywordHits) {
keypressEvent.stopPropagation();
choicesElement.setChoices([
{
value: keypressEvent.target.value,
label: keypressEvent.target.value,
selected: true,
},
], 'value', 'label', false);
const linterEvent = keypressEvent;
linterEvent.target.value = '';
}
}
});
};
Edit: I've just now realized I forgot to change the "No results found" caption to "Press enter to add" like the OP
As far as I can tell pressing enter always adds the content of the search field as new item, even when I try to select another element in the list via cursor keys.
That's kind of counterintuitive to me.
As far as I can tell pressing enter always adds the content of the search field as new item, even when I try to select another element in the list via cursor keys.
That's kind of counterintuitive to me.
I don't know if this issue has been fixed already or if you were using the code I've made but my fix is pretty modifiable and it's working fine in production, I'm pretty sure you can come up with a reasonable solution based on it.
I don't know if this issue has been fixed already or if you were using the code I've made but my fix is pretty modifiable and it's working fine in production, I'm pretty sure you can come up with a reasonable solution based on it.
I tried to use the pull request implementation, not yours. That'll be the next step for me ;)
... Few moments later:
@GoldenMaximo: Your implementation feels more like what I would expect.
As far as I can tell pressing enter always adds the content of the search field as new item, even when I try to select another element in the list via cursor keys.
That's kind of counterintuitive to me.
I agree this is counterintuitive - a solution to add choices dynamically needs to be able to differentiate between selecting an existing choice and adding a non-existent choice
Hi! Is there any update about this feature? Thanks
Hi @GoldenMaximo , I am trying your workaround but I get Cannot read property 'element' of undefined
. I have tried with both the Choices object and the select element on which I initialise Choices. Can you clarify how to use the workaround? Thanks!
You may have to use an older Version of this library to use the exact code example. I can confirm, that it worked for me, but back then choices.js was at version 7.x.x 6 months ago, now it is at 9.x.x.
Thanks @BlaM . I ended up using Tagify for tags.
why is a new option introduced? Why not just checking if the type is a multiple-select and addItems is set?
Edit: I misunderstood the addItems flag
I reworked this PR and made an other PR: https://github.com/jshjohnson/Choices/pull/856
There are many edge cases which need to be handled (like disabled options).
I'm confused, isn't this in the demo? Look at the first example here https://joshuajohnson.co.uk/Choices/
Is or isn't this actually supported?
Ok sorry, I understand now. Adding new elements only works for input
types, not select
. That wasn't very well documented.
Excuse me, when is this feature implemented ? I want to use this feature in my project. Can you implement this feature soon, please ? Thanks.
Please, I'm waiting for this feature too. I would like to keep using this awesome select box, but I need to add the user input when it's a select.
I think we need this feature for single select too, not just on tags
select
Like @rizkhal, I really need this feature in single select. Any news on when and if this will be merged? How can i help in speeding up this MR?
Sadly, this PR would need to be completely redone. This library has been completely revamped into TS. Merging this PR isn't possible without redoing most of it.
I re-implemented this PR under the new code base. #1117
@GoldenMaximo I like the workaround you've come out with. Will it be possible that you can share the full example as I'm newbie to javascript and not sure where should I include the "const choicesIssue39Fix" you've developed. Much Appreciated!
Waiting for this feature, are there any updates ?
I re-implemented this PR under the new code base. #1117
Thank you for your perfect implementation, and I hope the author can merge as soon as possible.
Made my own implementation (read: hack)
function choicesjs_allow_create(obj) {
var addNew = document.createElement("div");
var choiceList = obj.choiceList.element;
addNew.addEventListener('click', function (e) {
addItem();
})
obj.input.element.addEventListener('keyup', function (e) {
if (addNewAllowed()) {
obj.removeHighlightedItems();
obj._currentState.choices.map(elm => elm.active = false)
addNew.innerHTML = "Press ENTER to add: <b>" + obj._currentValue + "</b>";
addNew.className = "choices__item choices__item--choice";
addNew.style.display = "block";
addNew.dataset.choiceSelectable = '';
choiceList.append(addNew)
var code = (e.keyCode ? e.keyCode : e.which);
if (code == 13) {
e.preventDefault()
e.stopPropagation()
return false;
}
} else {
addNew.style.display = "none";
}
if (!choiceList.querySelectorAll(".is-highlighted").length) {
console.log("ASDF")
addNew.classList.add("is-highlighted", "choices__item--selectable");
}
})
obj.input.element.addEventListener('keydown', function (e) {
var code = (e.keyCode ? e.keyCode : e.which);
if (code == 13 && addNewAllowed()) {
addItem();
e.preventDefault()
e.stopPropagation()
return false;
}
})
function addNewAllowed() {
let choices_found = obj._currentState.choices.filter(elm => elm.label == obj._currentValue).length
let items_found = obj._currentState.items.filter(elm => elm.label == obj._currentValue).length
return (choices_found + items_found) == 0 && obj._currentValue != '';
}
function addItem() {
obj.removeHighlightedItems();
obj._currentState.choices.map(elm => elm.active = false)
obj.input.element.value = ''
obj._addChoice({
value: obj._currentValue,
label: obj._currentValue,
isSelected: true,
isDisabled: false
});
obj.input.element.dispatchEvent(new Event('keyup'));
}
}
choicesjs_allow_create(choisesObjVariable)