-
Notifications
You must be signed in to change notification settings - Fork 37
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 update_selection/1 to dynamically change the selection #69
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,11 +127,27 @@ defmodule LiveSelect do | |
send_update(LiveSelect.Component, id: live_select_id, value: new_selection) | ||
``` | ||
|
||
`new_selection` must be a single element in `:single` mode, a list in `:tags` mode. If it's `nil`, the selection will be cleared. | ||
After updating the selection, `LiveSelect` will trigger a change event in the form. | ||
`new_selection` must be a single element in `:single` mode, a list in `:tags` mode. If it's `nil`, the selection will be cleared. | ||
After updating the selection, `LiveSelect` will trigger a change event in the form. | ||
|
||
To set a custom id for the component to use with `Phoenix.LiveView.send_update/3`, you can pass the `id` assign to `live_select/1`. | ||
|
||
## Dynamically updating the selection | ||
|
||
You can also update the selection dynamically by passing an 1 arity function that receives the current selection to `:update_selection`: | ||
|
||
``` | ||
send_update(LiveSelect.Component, id: live_select_id, update_selection: fn current_selection -> Enum.filter(current_selection, &String.length(&1.label) > 3)) | ||
``` | ||
|
||
In this case, only the values with a label longer than 3 characters will be kept in the selection. | ||
|
||
Another example that appends values to the current selection: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need the second example |
||
|
||
``` | ||
values_to_append = [1, 2, 3] | ||
send_update(LiveSelect.Component, id: live_select_id, update_selection: fn current_selection -> current_selection ++ values_to_append end) | ||
``` | ||
|
||
## Examples | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,7 +151,7 @@ defmodule LiveSelect.Component do | |
socket, | ||
:selection, | ||
fn selection, %{options: options, mode: mode, value_mapper: value_mapper} -> | ||
update_selection( | ||
set_selection( | ||
field.value, | ||
selection, | ||
options, | ||
|
@@ -168,7 +168,19 @@ defmodule LiveSelect.Component do | |
if Map.has_key?(assigns, :value) do | ||
update(socket, :selection, fn | ||
selection, %{options: options, value: value, mode: mode, value_mapper: value_mapper} -> | ||
update_selection(value, selection, options, mode, value_mapper) | ||
set_selection(value, selection, options, mode, value_mapper) | ||
end) | ||
|> client_select(%{input_event: true}) | ||
else | ||
socket | ||
end | ||
|
||
socket = | ||
if Map.has_key?(assigns, :update_selection) do | ||
update(socket, :selection, fn | ||
selection, | ||
%{update_selection: update_fn, options: options, mode: mode, value_mapper: value_mapper} -> | ||
update_selection(update_fn, selection, options, mode, value_mapper) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My suggestion is to replace this line with:
Where We won't have dupe detection but I think it's overkill. We don't have it anyway when you pass the selection explicitly with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ran into a problem with this and still need to think of a way to figure it out. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry that was not clear, my function fixes the problem by splitting it and selectively running There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for clarifying. Ok, but I don't think the problem is actually fixed by your function. Take this example: current_selection = [%{value: 1, label: "one"}, %{value: 2, label: "two"}]
value_mapper = fn n -> %{value: n, label: to_string(n)} end
update_fn = fn current_selection -> current_selection ++ [2, 3] end With this arguments, your I can't think of an elegant solution for this. Can you? My impulse would be to say: it's the responsibility of the writer of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I thought about it and I think another, perhaps better option would to not call So something like:
|
||
end) | ||
|> client_select(%{input_event: true}) | ||
else | ||
|
@@ -365,6 +377,7 @@ defmodule LiveSelect.Component do | |
:clear_button, | ||
:hide_dropdown, | ||
:value_mapper, | ||
:update_selection, | ||
# for backwards compatibility | ||
:form | ||
] | ||
|
@@ -514,19 +527,38 @@ defmodule LiveSelect.Component do | |
}) | ||
end | ||
|
||
defp update_selection(nil, _current_selection, _options, _mode, _value_mapper), do: [] | ||
defp set_selection(nil, _current_selection, _options, _mode, _value_mapper), do: [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed this function to not cause confusion with the new option, as this function will effectively update the selection as a whole. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let us not rename it (see comment above) |
||
|
||
defp update_selection(value, current_selection, options, :single, value_mapper) do | ||
defp set_selection(value, current_selection, options, :single, value_mapper) do | ||
List.wrap(normalize_selection_value(value, options ++ current_selection, value_mapper)) | ||
end | ||
|
||
defp update_selection(value, current_selection, options, :tags, value_mapper) do | ||
defp set_selection(value, current_selection, options, :tags, value_mapper) do | ||
value = if Enumerable.impl_for(value), do: value, else: [value] | ||
|
||
Enum.map(value, &normalize_selection_value(&1, options ++ current_selection, value_mapper)) | ||
|> Enum.reject(&is_nil/1) | ||
end | ||
|
||
defp update_selection(update_fn, current_selection, options, _mode, value_mapper) | ||
when is_function(update_fn, 1) do | ||
new_selection = update_fn.(current_selection) | ||
|
||
{existing, new} = Enum.split_with(new_selection, &(&1 in current_selection)) | ||
|
||
new = | ||
Enum.map(new, &normalize_selection_value(&1, options, value_mapper)) | ||
|> Enum.reject(&is_nil/1) | ||
|
||
Enum.uniq(existing ++ new) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This forces uniqueness. Otherwise appending would allow duplicate values and I couldn't think of a reason to allow it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's remove this new function, see comment above |
||
end | ||
|
||
defp update_selection(_update_fn, _current_selection, _options, _mode, _value_mapper) do | ||
raise """ | ||
Option for `:update_selection` must be a function with arity 1 | ||
""" | ||
end | ||
|
||
defp normalize_selection_value(%Ecto.Changeset{action: :replace}, _options, _value_mapper), | ||
do: nil | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say: