solop
Autocomplete input component w/o Javascript
Hi,
i would like to share the following component that you can use in your phoenix forms to provide a input field with a customizable source of suggestions. Could be used as a tagging component etc.
defmodule MyProjectWeb.Components.SelectComponent do
use MyProjectWeb, :live_component
def render(assigns) do
~H"""
<div class="flex flex-row items-baseline gap-4 break-inside-avoid" phx-target={@myself}>
<.label><%= @label %></.label>
<div class="w-full">
<div class="flex flex-wrap items-center gap-1">
<.inputs_for :let={f} field={@form[@field_name]}>
<.tag_badge color={@color_function.(f[:type].value)}>
<%= f[:name].value %>
<:actions>
<button
type="button"
phx-click="autocomplete_remove"
phx-target={@myself}
phx-value-index={f.index}
class="hover:text-red-500 pl-1"
>
×
</button>
</:actions>
</.tag_badge>
<.input field={f[:id]} type="hidden" />
<.input field={f[:name]} type="hidden" />
<input type="hidden" name={"case[#{@field_name}_sort][]"} value={f.index} />
</.inputs_for>
</div>
<.input
field={@form[:"new_#{@field_name}"]}
type="text"
placeholder={@placeholder}
phx-keydown="autocomplete_key"
onkeydown="return (event.keyCode!=13);"
phx-target={@myself}
/>
<input type="hidden" name={"case[#{@field_name}_drop][]"} />
<div>
<%= for {suggestion, index} <- Enum.with_index(@suggestions) do %>
<div
class={"suggestion #{if index == @selected_index, do: "bg-indigo-200"} rounded-sm p-1"}
phx-click="autocomplete_select"
phx-target={@myself}
phx-value-index={index}
>
<%= suggestion.name %>
</div>
<% end %>
</div>
</div>
</div>
"""
end
def mount(socket) do
{:ok, assign(socket, suggestions: [], selected_index: 0)}
end
def update(assigns, socket) do
{:ok, assign(socket, assigns)}
end
def handle_event("autocomplete_key", %{"key" => key, "value" => value}, socket) do
case key do
"ArrowDown" ->
{:noreply, update_selected_index(socket, 1)}
"ArrowUp" ->
{:noreply, update_selected_index(socket, -1)}
"Enter" ->
selected = get_from_suggestions(socket, socket.assigns.selected_index) || %{name: value}
{:noreply, socket.assigns.on_select.(socket, selected)}
_ ->
suggestions = socket.assigns.search_function.(value)
{:noreply, assign(socket, suggestions: suggestions, selected_index: 0)}
end
end
## In real life there seems to be always a value, but to make tests easier
## this def allows events without value
def handle_event("autocomplete_key", %{"key" => key}, socket) do
handle_event("autocomplete_key", %{"key" => key, "value" => ""}, socket)
end
def handle_event("autocomplete_remove", %{"index" => index}, socket) do
{:noreply, socket.assigns.on_remove.(socket, index)}
end
def handle_event("autocomplete_select", %{"index" => index}, socket) do
{int, _} = Integer.parse(index)
selected = get_from_suggestions(socket, int)
{:noreply, socket.assigns.on_select.(socket, selected)}
end
defp get_from_suggestions(socket, index) do
Enum.at(socket.assigns.suggestions, index)
end
defp update_selected_index(socket, direction) do
update(
socket,
:selected_index,
&rem(
&1 + direction + length(socket.assigns.suggestions),
length(socket.assigns.suggestions)
)
)
end
end
use it like this:
<.live_component
module={SelectComponent}
id={@id}
form={@form}
field_name="tags"
label="Tags"
placeholder="Enter a tag"
search_function={&search_suggestions/1}
color_function={&get_color_for_actor/1}
on_select={&handle_select/2}
on_remove={&handle_remove/2}
/>
You need to define these callback functions for it to work..
Popular in Discussions
In general I’ve been sticking to this community style guide GitHub - christopheradams/elixir_style_guide: A community driven style guide ...
New
Elixir Upgrading is so Simple in Ubuntu and It worked for me
Ubuntu 16.04
git clone https://github.com/elixir-lang/elixir.git
cd elixir...
New
Hi all,
I am trying to convince my team to use liveview over the current react. What are some of the points where one should consider us...
New
It would be nice to be able to define a redirect from one route to another from the router.ex file. E.g.:
redirect "/", UserController, ...
New
I have put far too much effort into Dialyzer over the last year or so - and basically - I doubt it’s worth the effort.
It’s not as easy ...
New
Are there any Elixir or Erlang libraries that help with this? I’ve been thinking how streaming services like twitch have exploded recentl...
New
I’ve been hearing much about the new formatter and it’s something I have been keen to try.
I find examples buy far the most illuminating...
New
Hey everyone,
this has been brewing in my head some time and it came up again while reading Adopting Elixir.
GenServers, supervisors et...
New
We’re considering our architecture from a viewpoint of scaling our traffic heavily over the next 6 months. Our current deployment is runn...
New
Decided against including more info in the title, but the gist is that Plataformatec sponsored projects will continue with the assets bei...
New
Other popular topics
lets say i have a sample like
a = 20; b = 10;
if (a > b) do
{:ok, "a"}
end
if (a < b) do
{:ok, b}
end
if (a == b) do
{:ok, "equa...
New
Hello everyone,
I try to use an Javascript Event Handler in my root.html.leex file.
Therefore I created a function in the app.js file: ...
New
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
Hi everyone,
One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this:
...
New
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar.
I p...
New
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
Lets say I have map like this fetching from my database
%{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
Hi everyone!
I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









