maxmarcon / live_select

Dynamic (multi)selection field for LiveView
https://hex.pm/packages/live_select
Apache License 2.0
185 stars 35 forks source link

LiveSelect field should get initial values #10

Closed goblinJoel closed 1 year ago

goblinJoel commented 1 year ago

I would like to be able to restore selections that hadn't been submitted yet, or that encountered an error on submission, like other forms seem to do via their changesets.

For example, I have a tabbed interface where switching tabs switches what info and form you're looking at. This live select is the only one that loses your work if you click away (within the same liveview) without saving.

It may be possible to simply take the current value from the form data using Phoenix.HTML.Form.input_value/2 in the template.

maxmarcon commented 1 year ago

Hi @goblinJoel

this should work on master now. If you have time and want to test it, that'd be really cool!

<%= live_select form, :field, mode: :tags %>

Now if the field in the form has value ["B", "C"], then B and C will be selected initially.

Alternatively, initial selection can be set explicitly, bypassing whatever is in the form:

<%= live_select form, :field, mode: :tags, value: ["D", "E"] %>
maxmarcon commented 1 year ago

closed with 8c9b2a0a8a3326086eda89a06fc83da3651a3e6e

goblinJoel commented 1 year ago

Messed with this on main some on Friday, but didn't get it all the way working because my use-case is goofy and doesn't use changesets the normal way due to a thru-table situation. But yesterday, I was able to finish that and confirm this works for me on the latest release. I'm using the :value param approach. Thank you!

maxmarcon commented 1 year ago

Great! Thanks for the feedback!

sevab commented 1 year ago

@maxmarcon looks like the code in https://github.com/maxmarcon/live_select/commit/8c9b2a0a8a3326086eda89a06fc83da3651a3e6e has been changed, and there's no longer way to set an initial value that will then be overridden by user's selection.

Do you have any tips for an idiomatic approach to this problem? This is a typical use case where opening a form you would expect each input to have the exiting value already selected.

E.g. This obviously doesn't work anymore:

      <.live_select
        label="Nationality"
        value={@person.nationality_id}
        field={@form[:nationality_search]}
        options={[{"None", nil} | Cb.Countries.options_for_select]} />
maxmarcon commented 1 year ago

Yes, the value is meant to override what is in the form.

The solution is to set the default value in the form :)

sevab commented 1 year ago

@maxmarcon thanks! I know this is out of the scope of this issue now, but do you know by any chance how to set the default value on a form field, without having to add an additional field to the underlying schema of my struct (e.g. nationality_search)? If not, all good, thank you for your time and help so far!

maxmarcon commented 1 year ago

hey no worries! Happy to help. Well the form is generated from some data, and the data source will contain the value for the field.

so if the data for your form is an Ecto schema with a nationality_search field:

embedded_schema do 
   field(:nationality_search, :integer, default: @default_value)
end

(I'm assuming it's an embedded schema). Or you could assign a value to nationality_search before creating the form for it.

It depends on how you're creating your form.

sevab commented 1 year ago

Thanks!

I'm creating the form from an ecto schema changeset of a db record. Basic Phoenix LiveView crud form basically:

# person.ex
schema "people" do
  belongs_to(:nationality, Cb.Countries.Country, foreign_key: :nationality_id)
end

# person_live_form.ex:
def update(%{person: person} = assigns, socket) do
  {:ok, assign(socket, form: to_form(People.change_person(person)))}
end

I can certainly add a virtual field to the schema, in order to keep track of the live_select's value:

schema "people" do
  belongs_to(:nationality, Cb.Countries.Country, foreign_key: :nationality_id)

  field(:nationality_search, :integer, virtual: true)
end

# then in liveview:
def update(%{person: person} = assigns, socket) do
  changeset = People.change_person(person, %{nationality_search: person.nationality_id})
  {:ok, assign(socket, form: to_form(changeset))}
end

But this seems to be a poor choice to me, poluting db schema definition with virtual fields for front-end components.

If there was a way to modify the form struct, it would be nice as it wouldn't require to modify the underlying schema, e.g.:

def update(%{person: person} = assigns, socket) do
  form = to_form(People.change_person(person))
  form = Map.put(form, :nationality_search, person.nationality_id)
  {:ok, assign(socket, form: form)}
end

Though the above obviously doesn't work, as form is not a map, and I couldn't find any docs in phoenix_html to modifying the Phoenix.HTML.Form struct, but thought you might know.

This seems like a common scenario users of the package would need to handle, so if anybody has simple solutions, please share.

maxmarcon commented 1 year ago

I can certainly add a virtual field to the schema, in order to keep track of the live_select's value:

why do you need that? live_select could be used to enter nationality_id directly. No need for a virtual field.

maxmarcon commented 1 year ago
<.live_select field={@form[:nationality_id]} />
sevab commented 1 year ago

@maxmarcon ah, I misunderstood the docs 😅 I thought live_select maintains the value in a separate field named "#{field_name}_search" for it to work. All works now, thank you so much!

maxmarcon commented 1 year ago

You're welcome! No the actual field that will be selected is the one you pass with the field assign. But live_select does keep an extra field called #{field_name}_text_input to store the value of the text input box. You can ignore that one.