Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create new options on the fly from user-written values => add a callback for unknown choices #594

Closed
wants to merge 2 commits into from

Conversation

franke-mc
Copy link

@franke-mc franke-mc commented May 25, 2019

Description

Sometimes the user needs to be allowed to add some not-yet-existing options on the fly.

Motivation and Context

If this library is to allow the user to enter a set of freely-written tags, choices need to be created from the user input on the fly if they don't exist yet.
I guess the motivation is similar to the one for a tagging mode in Select2: https://select2.org/tagging , and related to pull-request #525 "Allow user-created choices for selects". So this addresses #39 as well. (We only discovered those issues after implementing our approach.)

In this implementation here, we provide a callback to be used when the user enters a value not yet available among the existing options, and allow the user to bypass the dropdown consultation entirely by holding the Shift key together with the Enter key. This approach is different from the ones mentioned above.

@caramiki: maybe you'd like to comment / compare?

How Has This Been Tested?

It works on multiple-value selects in our application. Something like the following callback function can be passed as a configuration option to Choices:

    // The following function gets 'this' from Choices ...
    var _this = this;
    const callbackOnUnknownChoice = function(value, hasShift) {
      var choices = this;
      const reject = function() {
        choices.dropdown.isActive = false;
        choices.showDropdown();
        choices.input.focus();
        choices.clearInput();
        return false;
      };
      if (this._store.activeItems.findIndex(x => (x.value.data.text === value)) > -1) {
        alert(`Tag "${value}" already selected!`);
        return reject();
      }
      var idx = this._store.choices.findIndex(x => (x.value.data.text === value));
      if (idx > -1) {
        const item = this._store.choices[idx];
        console.log(`Tag "${value}" already exists: `,item);
        this.highlightItem(item);
        const activeItems = this._store.activeItems;
        if (activeItems[0]) {
          activeItems[0].keyCode = 13;
        }
        // use the new parameter to directly pass the item id:
        this._handleChoiceAction(this._store.activeItems, null, item.id);
        return false;
      }
      if (!hasShift) {
        if (!confirm(`New Tag "${value}" ?`)) {
          return reject();
        }
      }
      const newTagObj = {"<placeholder-to-create-this-object-from>": value}
      const newItem = {
        label: value,
        value: newTagObj,
        keyCode: 13,
        isSelected: true,
        active: false
      };
      //console.log('Adding choice ',newItem);
      this.setChoices([newItem],'value','label',false);
      const idx = this._store.choices.findIndex(x => (x.value.data.text === value));
      if (idx > -1) {
        const item = this._store.choices[idx];
        //console.log('Activating item ',item.id);
        this.highlightItem(item);
        const activeItems = this._store.activeItems;
        if (activeItems[0]) {
          activeItems[0].keyCode = 13;
        }
        this._handleChoiceAction(this._store.activeItems, null, item.id);
      }
      else {
        console.warn('added choice not found: ',value);
      }
      this.clearInput();
      return true;
    };

Of course, this uses internal functions...

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

@jshjohnson jshjohnson added the feature Pull request that adds new functionality label Oct 30, 2019
@@ -812,13 +812,13 @@ class Choices {
this.input.focus();
}

_handleChoiceAction(activeItems, element) {
if (!activeItems || !element) {
_handleChoiceAction(activeItems, element, idDirectly) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused - I can't see idDirectly ever being passed to this function? It's name is also not particularly clear

Copy link
Author

@franke-mc franke-mc Nov 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jshjohnson : it appeared to make sense in our use case of a 'Tags' field: in the formio.js library, in a multiple-value Select component with the Choice options being other formio.js Resources, the requirement was to allow easy creation of a new Resource ('Tag'). We implemented it using this new callback, with Shift + Enter being the combination to force creating a new tag unconditionally.

In the "How Has This Been Tested?" section of this pull request, you can find the full callback source code. As you can see, idDirectly is used there. If you have an idea for a better name, or for a better way to expose that API, feel free to change as you like.

@jshjohnson
Copy link
Collaborator

Closing in favour of #525 as this solution relies too much on private methods/properties. Thanks 👍

@jshjohnson jshjohnson closed this Dec 23, 2019
@franke-mc
Copy link
Author

@jshjohnson: Thanks for following up on #525. When is that going to be available? Does it allow to configure some kind of hooks/callbacks, too? If not, can you please consider adding a callback there to be triggered whenever a user has successfully "written in" a new option?

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Pull request that adds new functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants