Skip to content

Commit

Permalink
Add Form.source_data/1 and Form.source_module/1
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieuprog committed May 31, 2024
1 parent 605523b commit 41a23cc
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 16 deletions.
24 changes: 8 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ They both render a hidden input for the `"__type__"` field.

### Displaying form inputs and errors in LiveView

You may use `polymorphic_embed_inputs_for/2` when working with LiveView.
You may use `PolymorphicEmbed.HTML.Component.polymorphic_embed_inputs_for/1` when working with LiveView, which functions similarly to [`Phoenix.Component.inputs_for/1`](Phoenix.Component.inputs_for).

```elixir
<.form
Expand All @@ -234,26 +234,18 @@ You may use `polymorphic_embed_inputs_for/2` when working with LiveView.
phx-change="validate"
phx-submit="save"
>
<%= for channel_form <- polymorphic_embed_inputs_for f, :channel do %>
<%= hidden_inputs_for(channel_form) %>
<.polymorphic_embed_inputs_for field={f[:channel]} :let={channel_form}>
<%= case source_module(channel_form) do %>
<% SMS -> %>
<.input field={channel_form[:number]} label="Number" />

<%= case get_polymorphic_type(f, :channel) do %>
<% :sms -> %>
<%= label channel_form, :number %>
<%= text_input channel_form, :number %>

<% :email -> %>
<%= label channel_form, :email_address %>
<%= text_input channel_form, :adress %>
<% Email -> %>
<.input field={channel_form[:address]} label="Email Address" />
<% end %>
<% end %>
</.polymorphic_embed_inputs_for>
</.form>
```

Using this function, you have to render the necessary hidden inputs manually as shown above.

There is also `PolymorphicEmbed.HTML.Component.polymorphic_embed_inputs_for/1`, which functions similarly to [`Phoenix.Component.inputs_for/1`](Phoenix.Component.inputs_for).

### Get the type of a polymorphic embed

Sometimes you need to serialize the polymorphic embed and, once in the front-end, need to distinguish them.
Expand Down
6 changes: 6 additions & 0 deletions lib/polymorphic_embed/html/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ if Code.ensure_loaded?(Phoenix.HTML) && Code.ensure_loaded?(Phoenix.HTML.Form) &

defdelegate get_polymorphic_type(form, field), to: PolymorphicEmbed.HTML.Helpers

defdelegate get_polymorphic_type(form_field), to: PolymorphicEmbed.HTML.Helpers

defdelegate source_data(form), to: PolymorphicEmbed.HTML.Helpers

defdelegate source_module(form), to: PolymorphicEmbed.HTML.Helpers

defdelegate to_form(source_changeset, form, field, options),
to: PolymorphicEmbed.HTML.Helpers

Expand Down
19 changes: 19 additions & 0 deletions lib/polymorphic_embed/html/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ if Code.ensure_loaded?(Phoenix.HTML) && Code.ensure_loaded?(Phoenix.HTML.Form) d
end
end

def get_polymorphic_type(%Phoenix.HTML.FormField{} = form_field) do
%{field: field_name, form: parent_form} = form_field
get_polymorphic_type(parent_form, field_name)
end

@doc """
Returns the source data structure
"""
def source_data(%Phoenix.HTML.Form{} = form) do
form.source.data
end

@doc """
Returns the source data structure
"""
def source_module(%Phoenix.HTML.Form{} = form) do
form.source.data.__struct__
end

def to_form(%{action: parent_action} = source_changeset, form, field, options) do
id = to_string(form.id <> "_#{field}")
name = to_string(form.name <> "[#{field}]")
Expand Down
47 changes: 47 additions & 0 deletions test/polymorphic_embed_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3124,6 +3124,53 @@ defmodule PolymorphicEmbedTest do
end
end

test "Form.source_data/1 and Form.source_module/1" do
reminder_module = get_module(Reminder, :polymorphic)

attrs = %{
date: ~U[2020-05-28 02:57:19Z],
text: "This is an Email reminder",
contexts: [
%{
__type__: "device",
ref: "12345",
type: "cellphone"
},
%{
__type__: "location",
age: "aquarius",
address: "some address"
}
]
}

changeset =
reminder_module
|> struct()
|> reminder_module.changeset(attrs)

safe_form_for(changeset, fn _f ->
safe_inputs_for(changeset, :contexts, :email, :polymorphic_with_type, fn f ->
PolymorphicEmbed.HTML.Form.get_polymorphic_type(f[:contexts])

case PolymorphicEmbed.HTML.Form.source_data(f) do
%PolymorphicEmbed.Reminder.Context.Device{} ->
assert PolymorphicEmbed.Reminder.Context.Device == PolymorphicEmbed.HTML.Form.source_module(f)

%PolymorphicEmbed.Reminder.Context.Location{} ->
assert PolymorphicEmbed.Reminder.Context.Location == PolymorphicEmbed.HTML.Form.source_module(f)

_ ->
assert false
end

1
end)

1
end)
end

describe "Form.get_polymorphic_type/3" do
test "returns type from changeset via identify_by_fields" do
reminder_module = get_module(Reminder, :polymorphic)
Expand Down

0 comments on commit 41a23cc

Please sign in to comment.