Readme
Spike.LiveView
provides a wrapper around
Phoenix.LiveView and
Phoenix.LiveComponent,
which simplifies working with memory-backed forms, including nested forms that require
contextual validation.
Installation
Available in Hex, the package can be installed
by adding spike_liveview
to your list of dependencies in mix.exs
:
def deps do
[
{:spike_liveview, "~> 0.2"}
]
end
Documentation can be found at https://hexdocs.pm/spike_liveview.
Quick start
Once installed in a Phoenix project, open up your *_web.ex
file and add the following
functions:
def form_live_view do
quote do
use Phoenix.LiveView,
layout: {MyAppWeb.LayoutView, "live.html"}
unquote(view_helpers())
use Spike.LiveView
end
end
def form_live_component do
quote do
use Phoenix.LiveComponent
unquote(view_helpers())
use Spike.LiveView
end
end
This allows you to build LiveViews and LiveComponents that ship with form and form erors handling capabilities out of the box.
You will need a Spike form. For usage how to build these, refer to Spike docs.
For example, your simplest possible registration form may look like this:
defmodule MyApp.RegistrationForm do
use Spike.Form do
field(:username, :string)
field(:password, :string)
validates(:username, presence: true, by: &__MODULE__.validate_not_taken/2)
validates(:password, presence: true)
end
def validate_not_taken(value, _context) do
if MyApp.Repo.get_by(MyApp.User, username: value) do
{:error, "username already taken"}
else
:ok
end
end
end
And a LiveView to handle registration process would be:
defmodule MyAppWeb.RegistrationLive do
use MyAppWeb, :form_live_view
import Spike.LiveView.Components
def mount(_params, _, socket) do
form = MyApp.RegistrationForm.new(%{})
{:ok,
socket
|> assign(%{form: form, errors: Spike.errors(form)})}
end
def render(assigns) do
~H"""
<h1>Register</h1>
<div>
<label for="username">Username:</label>
<.form_field field={:username} form={@form}>
<input id="username" name="value" type="text" value={@form.username} />
</.form_field>
<.errors let={field_errors} field={:username} form={@form} errors={@errors}>
<span class="error">
<%= field_errors |> Enum.map(fn {_k, v} -> v end) |> Enum.join(", ") %>
</span>
</.errors>
</div>
<div>
<label for="password">Password:</label>
<.form_field field={:password} form={@form}>
<input id="password" name="value" type="text" value={@form.password} />
</.form_field>
<.errors let={field_errors} field={:password} form={@form} errors={@errors}>
<span class="error">
<%= field_errors |> Enum.map(fn {_k, v} -> v end) |> Enum.join(", ") %>
</span>
</.errors>
</div>
<a href="#" phx-click="register">Register!</a>
"""
end
def handle_event("register", _, socket) do
if socket.assigns.errors == %{} do
# perform registration logic here
IO.puts("Registering user with #{socket.assigns.form.username} and #{socket.assigns.form.password}....")
{:noreply, socket}
else
{:noreply, socket |> assign(:form, Spike.make_dirty(socket.assigns.form))}
end
end
end
Remember to mount it at router and visit http://localhost:4000/register:
scope "/", MyAppWeb do
pipe_through :browser
live "/register", RegistrationLive
end
Usage of form components provided by this library is, however, pretty low-level, and we recommend you build your own form components instead.
For starting point to build your own form components, see our Components Library.
With the above components, we can shorten our render/1
function:
def render(assigns) do
~H"""
<h1>Register</h1>
<Input type="text" form={@form} field={:username} errors={@errors} />
<Input type="passwod" form={@form} field={:} errors={@errors} />
<a href="#" phx-click="submit">Register!</a>
"""
end
And that's pretty sweet!
See Components Library, Spike Example app for more examples.