# Readme
`Spike.LiveView` provides a wrapper around
[Phoenix.LiveView](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html) and
[Phoenix.LiveComponent](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveComponent.html),
which simplifies working with memory-backed forms, including nested forms that require
contextual validation.
## Installation
[Available in Hex](https://hex.pm/packages/spike_liveview), the package can be installed
by adding `spike_liveview` to your list of dependencies in `mix.exs`:
```elixir
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](https://hexdocs.pm/spike).
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](components_library.md).
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](components_library.md),
[Spike Example app](https://github.com/hubertlepicki/spike_example) for more examples.