View Source Components Library
This library only comes with building blocks for your components, and you should built your own form components - or use the provided building blocks directly.
Feel free to grab and customize these components to your needs, and treat them as a starting point when building your form builder.
The code below is released under public domain:
defmodule MyAppWeb.FormComponents do
use MyAppWeb, :component
import Spike.LiveView.Components
def errors_component(%{form: _, field: _, errors: _} = assigns) do
~H"""
<.errors let={field_errors} field={@field} form={@form} errors={@errors}>
<span class="error">
<%= field_errors |> Enum.map(fn {_k, v} -> v end) |> Enum.join(", ") %>
</span>
</.errors>
"""
end
def label_component(%{ref: _ref, text: _text, field: _field} = assigns) do
assigns = assign_new(assigns, :required, fn -> false end)
if assigns.required do
~H"""
<label for={"#{@ref}_#{@field}"}>* <%= @text %></label>
"""
else
~H"""
<label for={"#{@ref}_#{@field}"}><%= @text %></label>
"""
end
end
def input_component(%{type: "textarea", field: _, form: _, errors: _} = assigns) do
assigns = assigns |> assign_new(:target, fn -> nil end)
~H"""
<div>
<%= if @label do %>
<.label_component text={@label} ref={@form.ref} field={@field} required={is_required?(@form, @field)} />
<% end %>
<.form_field field={@field} form={@form} target={@target}>
<textarea id={"#{@form.ref}_#{@field}"} name="value"><%= @form |> Map.get(@field) %></textarea>
</.form_field>
<.errors_component form={@form} field={@field} errors={@errors} />
</div>
"""
end
def input_component(%{type: type, field: _, form: _, errors: _} = assigns)
when type in ["text", "password", "email"] do
assigns = assigns |> assign_new(:target, fn -> nil end)
~H"""
<div>
<%= if @label do %>
<.label_component text={@label} ref={@form.ref} field={@field} required={is_required?(@form, @field)} />
<% end %>
<.form_field field={@field} form={@form} target={@target}>
<input id={"#{@form.ref}_#{@field}"} name="value" type={type} value={@form |> Map.get(@field)} />
</.form_field>
<.errors_component form={@form} field={@field} errors={@errors} />
</div>
"""
end
def input_component(%{type: "checkbox", field: _, form: _, errors: _} = assigns) do
assigns =
assigns
|> assign_new(:checked_value, fn -> "1" end)
|> assign_new(:unchecked_value, fn -> "0" end)
|> assign_new(:target, fn -> nil end)
~H"""
<div>
<.form_field field={@field} form={@form} target={@target}>
<span class="float-left">
<input id={"#{@form.ref}_#{@field}_unchecked"} name="value" type="hidden" value={@unchecked_value} />
<input id={"#{@form.ref}_#{@field}"} name="value" type="checkbox" value={@checked_value} checked={is_checked?(@form, @field, @checked_value)} />
</span>
<span>
<%= if @label do %>
<.label_component text={@label} ref={@form.ref} field={@field} required={is_required?(@form, @field)} />
<% end %>
</span>
</.form_field>
<.errors_component form={@form} field={@field} errors={@errors} />
</div>
"""
end
def input_component(%{type: "select", field: _, form: _, errors: _, options: _} = assigns) do
assigns = assigns |> assign_new(:target, fn -> nil end)
~H"""
<div>
<%= if @label do %>
<.label_component text={@label} ref={@form.ref} field={@field} required={is_required?(@form, @field)} />
<% end %>
<.form_field field={@field} form={@form} target={@target}>
<select id={"#{@form.ref}_#{@field}"} name="value">
<%= for {value, text} <- @options do %>
<option value={value || ""} selected={@form |> Map.get(@field) == value}><%= text %></option>
<% end %>
</select>
</.form_field>
<.errors_component form={@form} field={@field} errors={@errors} />
</div>
"""
end
def input_component(%{type: "radio", field: _, form: _, errors: _, options: _} = assigns) do
assigns = assigns |> assign_new(:target, fn -> nil end)
~H"""
<div>
<%= if @label do %>
<.label_component text={@label} ref={@form.ref} field={@field} required={is_required?(@form, @field)} />
<% end %>
<.form_field field={@field} form={@form} target={@target}>
<%= for {{value, text}, index} <- Enum.with_index(@options) do %>
<span class="float-left">
<input name="value" id={"#{@form.ref}_#{@field}_#{index}"} type="radio" value={value || ""} checked={@form |> Map.get(@field) == value} />
</span>
<span>
<.label_component text={text} ref={@form.ref} field={"#{@field}_#{index}"} />
</span>
<div class="clearfix"></div>
<% end %>
</.form_field>
<.errors_component form={@form} field={@field} errors={@errors} />
</div>
"""
end
defp is_checked?(form, field, checked_value) do
Map.get(form, field) == checked_value || Map.get(form, field) == true
end
defp is_required?(form, field) do
validations = Vex.Extract.settings(form) |> Map.get(field, [])
{:presence, true} in validations
end
end
Also see Spike Example app for more examples.