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

Add new option dynamically #11

Closed
jaimeiniesta opened this issue Jan 18, 2023 · 13 comments
Closed

Add new option dynamically #11

jaimeiniesta opened this issue Jan 18, 2023 · 13 comments
Labels
enhancement New feature or request

Comments

@jaimeiniesta
Copy link

Hi, this is a followup of the conversation in https://elixirforum.com/t/liveselect-dynamic-selection-input-component-for-liveview/49538/29?u=jaimeiniesta

The context is, I want to add a tagging editor, where the user can choose from previous tags but also add new ones, like here:

https://fullstackphoenix.com/tutorials/tagging-interface-with-phoenix-liveview-and-tailwind-tagging-part-2

tut_15_img_0

As mentioned in the Elixir Forum thread:

It would be something like a “dynamic option mode”, where if you hit enter whatever you have typed will become a new selected option.

I have managed to make it more-or-less work, see video:

https://www.dropbox.com/s/a3ql5qs2zynggtj/live_select_2.mov?raw=1

But it won't work with the Enter key yet. What I did is:

<%= live_select f, :tag_search, mode: :tags, options: @live_select_options, placeholder: "Add tag" %>

and then listen to the ChangeMsg to replace the options:

  def handle_info(%LiveSelect.ChangeMsg{field: :tag_search, text: text}, socket) do
    new_live_select_options = [text | socket.assigns.user_tags]

    {:noreply, assign(socket, live_select_options: new_live_select_options)}
  end

Where user_tags is the original list of options from the existent user tags.

So for me what's left is being able to add new options with the "Enter" key, which should also clear the input field.

@jaimeiniesta
Copy link
Author

Bonus point: hitting the comma key , should also add the new option. Maybe even pasting in a comma-separated string. Maybe the tag separator should be configurable, other people may prefer to use spaces for example.

Example:

I want to add the new tags "improvement, changelog, pro". Just by typing that, the three tags "improvement", "changelog" and "pro" should appear as selected options in the tag list.

@maxmarcon maxmarcon added the enhancement New feature or request label Jan 19, 2023
@maxmarcon
Copy link
Owner

I implemented it. The option is called user_defined_options. If set to true, typing text and hitting enter will result in a new tag being added if there are no entries in the dropdown. You can try this out in the showcase app. To use it in your application, you'll have to point your hex dependency to the github repo. Let me know what you think

There's still at least one issue left to resolve: if the asynchronous update of the dropdown is slow, there could be a race condition where the user might hit enter before the content of the dropdown is rendered, resulting in a new tag being added when in reality an existing one should have been selected. I'll try to fix this tonight.

@maxmarcon
Copy link
Owner

Ok I fixed the race condition. You can try the main branch and see if this new feature works for you :)

@jaimeiniesta
Copy link
Author

a new tag being added if there are no entries in the dropdown

I think this can lead to unexpected results, I would expect that as I type, the partial result appears live in the options, if I hit enter then it gets added.

Let's imagine there was a city called "Barce". I can't add it because it tries to autocomplete with "Barcelona":

Captura de pantalla 2023-01-25 a las 8 21 44

Instead, I would expect to have both Barce and Barcelona options for me to choose.

@jaimeiniesta
Copy link
Author

I recorded a video explaining the above behavior, this is what I have working on now:

https://www.dropbox.com/s/xaj7qqj28p72395/live_select_3.mov?raw=1

@maxmarcon
Copy link
Owner

I was expecting you to say that 😃

yes I also thought about it after implementing it and it makes more sense to always add the tag (unless it's already in the selection).

I updated, please give it another try

@jaimeiniesta
Copy link
Author

Cool, thanks!

I'm checking the demo, now it works when you press Enter, that's great.

I still think that it should be added to the list of available options, as in my video above. That would feel more complete to me, as it would behave like the other options that can be picked with the keyboard or mouse click.

@maxmarcon
Copy link
Owner

maxmarcon commented Jan 25, 2023

I still think that it should be added to the list of available options, as in my video above. That would feel more complete to me, as it would behave like the other options that can be picked with the keyboard or mouse click.

Already selected options can't be selected again so they would be just unselectable options in the dropdown.

if you still want to see them, you can keep track of them by hooking on the form's change event:

def handle_event("change", %{"form" => %{"live_select_input" => selected_tags}}, socket) do 
   # assuming you're keeping the list of options in the "options" assign  
   # and the id of the live select input in the "live_select_id" assign
   socket = update(socket, :options, & Enum.uniq(&1 ++ selected_tags))

   send_update(LiveSelect.Component, id: socket.assigns.live_select_id, options: socket.assigns.options)
  
   {:noreply, socket}
end

def handle_info(%ChangeMsg{} = change_msg, socket) do 

  send_update(LiveSelect.Component, id: change_msg.id, options: socket.assigns.options)

  {:noreply, socket}
end

You can now assign a custom id when you call live_select/3 so you can communicate with the component via send_update/3. This will become the main way to update the options, I'm planning to remove the old ChangeMsg, update_options update loop.

hope this helps

@maxmarcon
Copy link
Owner

closed with 711ae39

@rxndxm
Copy link

rxndxm commented Aug 23, 2023

@maxmarcon loving what you've started here! is there a known way to get the selected options to appear inside the search input, as shown in the gif above:

Alt Text

@maxmarcon
Copy link
Owner

Hi @rxndxm. This actually was the plan in the beginning but I wasn't able to find an easy way to implement it. If you can think of a way to implement it and making the input field expand vertically as it gets filled with tags, I'd be very interested. It would be a nice addition.

@rxndxm
Copy link

rxndxm commented Aug 23, 2023

@maxmarcon Something like this:

If you add a width to the <multi-input> on that page, it works nicely.

The selected items are slotted just before the input element:

<multi-input>
     <div class="item">JavaScript</div>
     <div class="item">Swift</div>
     <div class="item">Java</div>
     <div class="item">PHP</div>
     <div class="item">Objective-C</div>
     <div class="item">Ruby</div>
     <div class="item">Python</div>
     <input list="languages">
     <datalist id="languages">
          <option value="C/C++"></option>
          <option value="SQL"></option>
     </datalist>
</multi-input>

I haven't looked at the live_select code yet, but would something like that work?

@rxndxm
Copy link

rxndxm commented Aug 23, 2023

Looking more closely these might be better examples :)

https://codepen.io/emah/pen/MWyXqqM
https://codepen.io/smhigley/pen/GRgjRVN

For this second one, looks like the parent is a flexbox, with the selected options having a max-width set, and the input having flex: 1 1 35%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants