Skip to content

Instantly share code, notes, and snippets.

@jamiedumont
Last active August 24, 2020 11:51
Show Gist options
  • Select an option

  • Save jamiedumont/13a803895726cb58141148f82afc65e3 to your computer and use it in GitHub Desktop.

Select an option

Save jamiedumont/13a803895726cb58141148f82afc65e3 to your computer and use it in GitHub Desktop.
"Typeahead" style autocomplete in LiveView form
defmodule RecordWeb.CompositeComponent do
use RecordWeb, :live_component
alias Record.Movements
alias Record.Core.Composite
import Ecto.Changeset
def update(assigns, socket) do
movement = case input_value(assigns.composite_form, :movement_id) do
nil -> nil
movement_id -> Movements.get_movement!(movement_id)
end
{:ok,
socket
|> assign(assigns)
|> assign(:movement, movement)
}
end
def render(assigns) do
RecordWeb.WorkoutView.render("define_composite.html", assigns)
end
end
<div>
<h2>New Composite</h2>
<%= hidden_input @composite_form, :uuid %>
<%= if is_nil(@movement) do %>
<div>
<label>Search movement</label>
<%= text_input @el_form, :movement_query, list: "results" %>
<div>
<h3>Search results</h3>
<%= for m <- @movement_results do %>
<button
type="button"
phx-target="#element_definition"
phx-click="add-movement"
phx-value-composite-id="<%= input_value(@composite_form, :uuid) %>"
phx-value-movement-id="<%= m.id %>"
><%= m.name %></button>
<% end %>
</div>
</div>
<% end %>
<%= if !is_nil(@movement) do %>
<%= hidden_input @composite_form, :movement_id %>
<% primary_units = Enum.filter(@movement.movement_units, fn m -> m.type == "primary" end) %>
<% secondary_units = Enum.filter(@movement.movement_units, fn m -> m.type == "secondary" end) %>
<% tertiary_units = Enum.filter(@movement.movement_units, fn m -> m.type == "tertiary" end) %>
<div>
<% movement_id = input_value(@composite_form, :movement_id) %>
<%= number_input @composite_form, :primary_quantity %>
<%= select @composite_form, :primary_unit, Enum.map(primary_units, &{&1.unit.name, &1.unit.id}) %>
<span><%= @movement.name %></span>
<%= if !Enum.empty?(secondary_units) do %>
<span> @ </span>
<%= number_input @composite_form, :secondary_quantity %>
<%= select @composite_form, :secondary_unit, Enum.map(secondary_units, &{&1.unit.name, &1.unit.id}) %>
<% end %>
<%= if !Enum.empty?(tertiary_units) do %>
<span> &amp; </span>
<%= number_input @composite_form, :tertiary_quantity %>
<%= select @composite_form, :tertiary_unit, Enum.map(tertiary_units, &{&1.unit.name, &1.unit.id}) %>
<% end %>
</div>
<% end %>
</div>
defmodule RecordWeb.ElementDefinitionComponent do
use RecordWeb, :live_component
alias Record.Core.{Element, Composite, Interval}
alias Record.Movements
alias RecordWeb.CompositeComponent
alias Ecto.Changeset
def update(assigns, socket) do
workout = assigns.workout
params = %{}
changeset = Element.changeset(assigns.element, params)
movement_query = ""
movement_results = []
{:ok,
socket
|> assign(assigns)
|> assign(:element_changeset, changeset)
|> assign(:movement_results, [])
|> render_add_composite()
}
end
defp render_add_composite(socket) do
render_add_composite =
socket.assigns.element_changeset
|> Ecto.Changeset.get_field(:composites)
|> Enum.all?(fn c ->
!is_nil(c.movement_id)
end)
assign(socket, :render_add_composite, render_add_composite)
end
def render(assigns) do
~L"""
<div id="<%= @id %>">
<h2>Element definition</h2>
<%= e = form_for @element_changeset, "#", [phx_target: @myself, phx_change: :validate, phx_submit: :save] %>
<%= label e, :type %>
<%= select e, :type, ["Please select...", "amrap", "amrep", "for_time", "emom", "tabata", "ladder", "xlet"] %>
<%= for c <- inputs_for(e, :composites, []) do %>
<%= input_value(c, :uuid) %>
<%= live_component @socket, CompositeComponent, id: input_value(c, :uuid), composite_form: c, movement_results: @movement_results, el_form: e %>
<% end %>
<%= if @render_add_composite do %>
<button type="button" phx-target="<%= @myself %>" phx-click="add-composite">Add composite</button>
<% end %>
</form>
</div>
"""
end
def handle_event("add-movement", %{"movement-id" => movement_id, "composite-id" => comp_id}, socket) do
IO.inspect(movement_id, label: "adding movement")
# Clear search query
changeset =
socket.assigns.element_changeset
|> Ecto.Changeset.put_change(:movement_query, "")
existing_composites = Ecto.Changeset.get_field(changeset, :composites)
updated_c =
existing_composites
|> Enum.find(fn c -> c.uuid == comp_id end)
|> Map.put(:movement_id, movement_id)
index =
existing_composites
|> Enum.find_index(fn c -> c.uuid == comp_id end)
new_composites =
List.replace_at(existing_composites, index, updated_c)
changeset =
changeset
|> Ecto.Changeset.put_embed(:composites, new_composites)
{:noreply,
socket
|> assign(:movement_results, [])
|> assign(:element_changeset, changeset)
|> render_add_composite()
}
end
def handle_event("add-composite", values, socket) do
element_changeset = socket.assigns.element_changeset |> IO.inspect(label: "changeset at add-com")
existing_composites = Ecto.Changeset.get_field(element_changeset, :composites)
composites =
existing_composites
|> Enum.concat([
Composite.new()
])
|> IO.inspect(label: "updated comp at add-com")
changeset =
element_changeset
|> Ecto.Changeset.put_embed(:composites, composites)
{:noreply,
socket
|> assign(:element_changeset, changeset)
|> render_add_composite()
}
end
def handle_event("validate", %{"element" => params}, socket) do
IO.inspect(params, label: "validate with params")
changeset = Element.changeset(socket.assigns.element_changeset, params)
movement_results =
case params["movement_query"] do
"" -> []
query -> Movements.search(query)
end
{:noreply,
socket
|> assign(:element_changeset, changeset)
|> assign(:movement_results, movement_results)
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment