Created
June 24, 2024 16:00
-
-
Save kevinschweikert/d6f768bff3876b1667232ebea63bfda4 to your computer and use it in GitHub Desktop.
Phoenix LiveView filter toogle all problem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Mix.install([ | |
{:phoenix_playground, git: "https://github.com/phoenix-playground/phoenix_playground"}, | |
{:ecto, "~> 3.11"}, | |
{:phoenix_ecto, "~> 4.6"} | |
]) | |
defmodule CoreComponents do | |
use Phoenix.Component | |
attr(:id, :any) | |
attr(:name, :any) | |
attr(:label, :string, default: nil) | |
attr(:field, Phoenix.HTML.FormField, | |
doc: "a form field struct retrieved from the form, for example: @form[:email]" | |
) | |
attr(:errors, :list) | |
attr(:required, :boolean, default: false) | |
attr(:options, :list, doc: "...") | |
attr(:rest, :global, include: ~w(disabled form readonly)) | |
attr(:class, :string, default: nil) | |
def checkgroup(assigns) do | |
new_assigns = | |
assigns | |
|> assign(:multiple, true) | |
|> assign(:type, "checkgroup") | |
input(new_assigns) | |
end | |
attr(:id, :any, default: nil) | |
attr(:name, :any) | |
attr(:class, :string, default: nil) | |
attr(:label, :string, default: nil) | |
attr(:type, :string, | |
default: "text", | |
values: ~w(checkbox color date datetime-local email file hidden month number password | |
range radio search select tel text textarea time url week checkgroup) | |
) | |
attr(:value, :any) | |
attr(:field, Phoenix.HTML.FormField, | |
doc: "a form field struct retrieved from the form, for example: @form[:email]" | |
) | |
attr(:checked, :boolean, doc: "the checked flag for checkbox inputs") | |
attr(:prompt, :string, default: nil, doc: "the prompt for select inputs") | |
attr(:options, :list, doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2") | |
attr(:multiple, :boolean, default: false, doc: "the multiple flag for select inputs") | |
attr(:rest, :global, include: ~w(autocomplete cols disabled form max maxlength min minlength | |
pattern placeholder readonly required rows size step)) | |
def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do | |
assigns | |
|> assign(field: nil, id: assigns.id || field.id) | |
|> assign_new(:name, fn -> if assigns.multiple, do: field.name <> "[]", else: field.name end) | |
|> assign_new(:value, fn -> field.value end) | |
|> input() | |
end | |
def input(%{type: "checkgroup"} = assigns) do | |
~H""" | |
<div phx-feedback-for={@name}> | |
<input type="hidden" name={@name} value="" /> | |
<div :for={{label, value} <- @options}> | |
<input | |
type="checkbox" | |
id={"#{@name}-#{value}"} | |
name={@name} | |
value={value} | |
checked={value in @value} | |
{@rest} | |
/> | |
<%= label %> | |
</div> | |
</div> | |
""" | |
end | |
def input(%{type: "checkbox"} = assigns) do | |
assigns = | |
assign_new(assigns, :checked, fn -> | |
Phoenix.HTML.Form.normalize_value("checkbox", assigns[:value]) | |
end) | |
~H""" | |
<div> | |
<label> | |
<input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} /> | |
<input type="checkbox" id={@id} name={@name} value="true" checked={@checked} {@rest} /> | |
<%= @label %> | |
</label> | |
</div> | |
""" | |
end | |
end | |
defmodule DemoLive do | |
use Phoenix.LiveView | |
use Phoenix.VerifiedRoutes, | |
router: PhoenixPlayground.Router.LiveRouter, | |
endpoint: PhoenixPlayground.Endpoint | |
import CoreComponents | |
defmodule Category do | |
defstruct [:id, :name] | |
end | |
def mount(_params, _session, socket) do | |
categories = [%Category{id: 0, name: "Category 1"}, %Category{id: 1, name: "Category 2"}] | |
categories_form = parse_filter(%{all: false, selected: [0]}) | |
categories_filter = apply_filter(categories_form) | |
categories_options = Enum.map(categories, &{&1.name, &1.id}) | |
{:ok, | |
assign(socket, | |
categories: categories, | |
filtered_categories: filter_categories(categories, categories_filter), | |
filter: categories_filter, | |
options: categories_options | |
) | |
|> assign_form(categories_form)} | |
end | |
def render(assigns) do | |
~H""" | |
<div :for={c <- @filtered_categories}> | |
<%= c.name %> | |
</div> | |
<.form id="categories" for={@categories_form} phx-change="change-categories"> | |
<.input type="checkbox" field={@categories_form[:all]} label="Show all" /> | |
<.checkgroup field={@categories_form[:selected]} options={@options} label="Categories" /> | |
</.form> | |
""" | |
end | |
def handle_params(params, _, socket) do | |
form = parse_filter(socket.assigns.filter, params) | |
filter = apply_filter(form) | |
{:noreply, | |
socket | |
|> assign( | |
filter: filter, | |
filtered_categories: filter_categories(socket.assigns.categories, filter) | |
) | |
|> assign_form(form)} | |
end | |
def handle_event("change-categories", %{"categories" => params}, socket) do | |
form = parse_filter(socket.assigns.filter, params) | |
filter = apply_filter(form) | |
{:noreply, socket |> push_patch(to: ~p"/?#{filter}")} | |
end | |
defp parse_filter(default_filter, attrs \\ %{}) do | |
fields = %{ | |
all: :boolean, | |
selected: {:array, :integer} | |
} | |
{default_filter, fields} | |
|> Ecto.Changeset.cast(attrs, Map.keys(fields)) | |
|> Map.put(:action, :validate) | |
end | |
defp apply_filter(%Ecto.Changeset{} = changeset) do | |
changeset | |
|> Ecto.Changeset.apply_changes() | |
end | |
defp assign_form(socket, %Ecto.Changeset{} = changeset) do | |
assign(socket, :categories_form, to_form(changeset, as: :categories)) | |
end | |
defp filter_categories(categories, %{all: all, selected: selected_ids}) do | |
if all do | |
categories | |
else | |
Enum.filter(categories, fn category -> category.id in selected_ids end) | |
end | |
end | |
end | |
PhoenixPlayground.start(live: DemoLive, port: 4003) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Solution see: https://gist.github.com/LostKobrakai/c13883df724543ac8258b9cce1399e6f