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

Dropdown/TagBox - Dynamically create new options from user input #5696

Open
JaneSjs opened this issue Feb 20, 2023 · 17 comments · May be fixed by #9618
Open

Dropdown/TagBox - Dynamically create new options from user input #5696

JaneSjs opened this issue Feb 20, 2023 · 17 comments · May be fixed by #9618
Assignees

Comments

@JaneSjs
Copy link
Contributor

JaneSjs commented Feb 20, 2023

https://www.figma.com/design/jcuAUsN02n2yJerx39mqf9/Library-Previews?node-id=3014-2573

To discuss:

  • Item IDs
    • Should we generate IDs automatically based on user input, or should we ask users to specify them? - generate IDs automatically based on user input
    • What to do if a duplicate ID is entered/generated? - Creating a new element will not be available.
  • ItemValue objects can have custom fields, such as "score". What to do with them? - No support.
  • Item editing - No support.
  • Item removal - No support.
  • Should we automatically create custom items based on survey.data? - No support.
  • How to save custom items? An event can be only specified outside Survey Creator, disabling a survey author to configure this feature in full. - Provided an event where you need more control over what happens when creating new options.

Consider dynamic item creation from user input. For example, as it's done for Select2: https://select2.org/tagging.

Source issue: #5689

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Apr 11, 2023

+1 T12664 - Dropdown value as text if value not in list
https://surveyjs.answerdesk.io/internal/ticket/details/T12664

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Sep 25, 2023

+2 #6964

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Nov 2, 2023

+1 T15375 - How to replicate a Select2 using the JQuery version of SurveyJS
https://surveyjs.answerdesk.io/internal/ticket/details/T15375

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Mar 6, 2024

+1 T16951 - User's answer does not exist in option list - dropdown list question
https://surveyjs.answerdesk.io/internal/ticket/details/T16951

Usage Scenario: Dropdown question should display an option even if it is not listed in a choices array.

@andrewtelnov
Copy link
Member

@andrewtelnov
Copy link
Member

  • 1 I need it for Presets and it will be better if we use it in our Property Grid instead of text question with dataList property

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Aug 15, 2024

@masciugo
Copy link
Contributor

we can still hope! this is the only reason I'm still using custom widget... thank you guys

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Nov 4, 2024

+1 T20547 - Updating params in custom widget
https://surveyjs.answerdesk.io/internal/ticket/details/T20547

@masciugo
Copy link
Contributor

Hi. Is there any plan to release this feature in the near/medium term? Thank you.

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Feb 11, 2025

T21812 - invisible choices/items
https://surveyjs.answerdesk.io/internal/ticket/details/T21812

@JaneSjs JaneSjs added the v2.0+ label Feb 11, 2025
@masciugo
Copy link
Contributor

T21812 - invisible choices/items https://surveyjs.answerdesk.io/internal/ticket/details/T21812

it's a private ticket. Could you state that the feature will be available in the v2?

thank you

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Feb 27, 2025

Hi @masciugo,
Unfortunately, this feature is not planned for v2.0.

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Feb 28, 2025

T22106 - How to allow users to add custom values to the list in tagbox
https://surveyjs.answerdesk.io/internal/ticket/details/T22106

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Feb 28, 2025

Custom Dropdown which creates new items from a user input

The following demo uses React-Creatable and shows how to implement a custom dropdown question which enables users to add non-existing items: View CodeSandbox.

import Creatable, { useCreatable } from "react-select/creatable";
import { Serializer, RendererFactory, ItemValue } from "survey-core";
import { SurveyQuestionDropdown, ReactQuestionFactory } from "survey-react-ui";
import "survey-core/defaultV2.css";
import "survey-creator-core/survey-creator-core.css";
import "./index.css";

class CustomSelect extends SurveyQuestionDropdown {
  constructor(props) {
    super(props);
  }
  get options() {
    return this.question.visibleChoices.map((c) => {
      return { value: c.value, label: c.value };
    });
  }
  get selectedOption() {
    return this.options.filter((o) => o.value == this.question.value);
  }
  onSelectChange = (selectedOption) => {
    this.setValueCore(selectedOption.value);
  };
  onCreateOption = (inputValue) => {
    if (!inputValue.trim()) return;

    // Check if value already exists
    const exists = this.question.choices.some(
      (choice) => choice.value === inputValue
    );
    if (!exists) {
      this.question.choices.push(
        new ItemValue({ value: inputValue, text: inputValue })
      );
    }

    this.setValueCore(inputValue);
  };
  renderElement() {
    if (this.isDisplayMode) {
      return (
        <div
          id={this.question.inputId}
          className={this.question.getControlClass()}
          disabled
        >
          {this.question.displayValue || this.question.placeholder}
        </div>
      );
    }
    return (
      <Creatable
        id={this.question.inputId}
        value={this.selectedOption}
        onChange={this.onSelectChange}
        onCreateOption={this.onCreateOption}
        options={this.options}
        required={this.question.isRequired}
        menuPortalTarget={document.body}
      />
    );
  }
}

// Register the CustomSelect component as a renderer under a custom name "sv-dropdown-react"
ReactQuestionFactory.Instance.registerQuestion("sv-dropdown-react", (props) => {
  return React.createElement(CustomSelect, props);
});

// Register "sv-dropdown-react" as a renderer for questions whose `type` is "dropdown" and `renderAs` property is "dropdown-react"
RendererFactory.Instance.registerRenderer(
  "dropdown",
  "dropdown-react",
  "sv-dropdown-react"
);

// Set "dropdown-react" as a default value for the `renderAs` property of all "dropdown" questions
Serializer.findProperty("dropdown", "renderAs").defaultValue = "dropdown-react";

@JaneSjs
Copy link
Contributor Author

JaneSjs commented Feb 28, 2025

A Custom Tagbox which creates new items from a user input

The following demo uses React-Creatable and shows how to implement a custom Tagbox question which enables users to add non-existing items: View Demo.

import Creatable from "react-select/creatable";
import { Serializer, RendererFactory, ItemValue } from "survey-core";
import { SurveyQuestionTagbox, ReactQuestionFactory } from "survey-react-ui";
import "survey-core/defaultV2.css";
import "survey-creator-core/survey-creator-core.css";
import "./index.css";

class CustomTagbox extends SurveyQuestionTagbox {
  constructor(props) {
    super(props);
  }
  get options() {
    return this.question.visibleChoices.map((c) => {
      return { value: c.value, label: c.value };
    });
  }
  get selectedOptions() {
    return Array.isArray(this.question.value)
      ? this.question.value.map(
          (val) =>
            this.options.find((o) => o.value === val) || {
              value: val,
              label: val,
            }
        )
      : [];
  }
  onSelectChange = (selectedOptions) => {
    const values = selectedOptions ? selectedOptions.map((o) => o.value) : [];
    this.setValueCore(values);
  };

  onCreateOption = (inputValue) => {
    if (!inputValue.trim()) return;

    const inputValueExists = this.question.choices.some(
      (choice) => choice.value === inputValue
    );
    const newItem = new ItemValue(inputValue, inputValue);
    if (!inputValueExists) {
      this.question.choices.push(newItem);
    }
    const newValues = [...(this.question.value || []), inputValue];
    this.setValueCore(newValues);
  };

  renderElement() {
    if (this.isDisplayMode) {
      return (
        <div
          id={this.question.inputId}
          className={this.question.getControlClass()}
          disabled
        >
          {this.question.displayValue || this.question.placeholder}
        </div>
      );
    }
    return (
      <Creatable
        id={this.question.inputId}
        value={this.selectedOptions}
        onChange={this.onSelectChange}
        onCreateOption={this.onCreateOption}
        options={this.options}
        required={this.question.isRequired}
        menuPortalTarget={document.body}
        isMulti={true}
      />
    );
  }
}

ReactQuestionFactory.Instance.registerQuestion("sv-tagbox-react", (props) => {
  return React.createElement(CustomTagbox, props);
});

RendererFactory.Instance.registerRenderer(
  "tagbox",
  "tagbox-react",
  "sv-tagbox-react"
);

Serializer.findProperty("tagbox", "renderAs").defaultValue = "tagbox-react";

@RomanTsukanov
Copy link
Contributor

@OlgaLarina
Proposed API:

  • Dropdown/Tag Box: allowCustomChoices: boolean
  • Survey: onCreateCustomChoiceItem
    • options.question: Question or a more suitable base class for Dropdown and TagBox
    • options.item: ItemValue
    • options.allow: boolean - users can set this property to false if they do not want to add this item.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants