Skip to content

Commit

Permalink
#2 mix phx.gen.live - create LiveView files
Browse files Browse the repository at this point in the history
  • Loading branch information
nelsonic committed Sep 22, 2023
1 parent 6c38ddb commit 4701c18
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 0 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,54 @@ a registration form
for our fictitious
**_Awesome_ Conference**.

# 4. Create `attendee` schema

The goal is to allow `people` attending **_Awesome_ Conf**
to submit the following data:

+ `first_name` - how we greet you. Will appear on your conference pass.
+ `last_name` - your family name. Will appear on you conference pass.
+ `email` - to confirm attendance
+ `phone_number` - to verify your access when attending the secret event.
+ `address` - so we can send the welcome pack
+ `address_line_2` - if your address has multiple lines.
+ `postcode` - for the address.
+ `gender` - for venue capacity planning.
+ `dietary_preference` - for meals and snacks provided by the conference.
+ `website` - share your awesomeness and have it as a QR code on your conference pass.
+ `description` - brief description of your awesome project.
+ `feedback` - Feedback or suggestions


## 4.1 `gen.schema`

Using the
[`mix phx.gen.live`](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Schema.html)
command,
run:
```sh
mix phx.gen.live Accounts Attendee attendees first_name:string last_name:string email:string --no-context
```

You should expect to see output similar to the following:

```sh
* creating lib/fields_demo_web/live/attendee_live/show.ex
* creating lib/fields_demo_web/live/attendee_live/index.ex
* creating lib/fields_demo_web/live/attendee_live/form_component.ex
* creating lib/fields_demo_web/live/attendee_live/index.html.heex
* creating lib/fields_demo_web/live/attendee_live/show.html.heex
* creating test/fields_demo_web/live/attendee_live_test.exs

Add the live routes to your browser scope in lib/fields_demo_web/router.ex:

live "/attendees", AttendeeLive.Index, :index
live "/attendees/new", AttendeeLive.Index, :new
live "/attendees/:id/edit", AttendeeLive.Index, :edit

live "/attendees/:id", AttendeeLive.Show, :show
live "/attendees/:id/show/edit", AttendeeLive.Show, :edit
```


# FieldsDemo
Expand Down
92 changes: 92 additions & 0 deletions lib/fields_demo_web/live/attendee_live/form_component.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
defmodule FieldsDemoWeb.AttendeeLive.FormComponent do
use FieldsDemoWeb, :live_component

alias FieldsDemo.Accounts

@impl true
def render(assigns) do
~H"""
<div>
<.header>
<%= @title %>
<:subtitle>Use this form to manage attendee records in your database.</:subtitle>
</.header>
<.simple_form
for={@form}
id="attendee-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save"
>
<.input field={@form[:first_name]} type="text" label="First name" />
<.input field={@form[:last_name]} type="text" label="Last name" />
<.input field={@form[:email]} type="text" label="Email" />
<:actions>
<.button phx-disable-with="Saving...">Save Attendee</.button>
</:actions>
</.simple_form>
</div>
"""
end

@impl true
def update(%{attendee: attendee} = assigns, socket) do
changeset = Accounts.change_attendee(attendee)

{:ok,
socket
|> assign(assigns)
|> assign_form(changeset)}
end

@impl true
def handle_event("validate", %{"attendee" => attendee_params}, socket) do
changeset =
socket.assigns.attendee
|> Accounts.change_attendee(attendee_params)
|> Map.put(:action, :validate)

{:noreply, assign_form(socket, changeset)}
end

def handle_event("save", %{"attendee" => attendee_params}, socket) do
save_attendee(socket, socket.assigns.action, attendee_params)
end

defp save_attendee(socket, :edit, attendee_params) do
case Accounts.update_attendee(socket.assigns.attendee, attendee_params) do
{:ok, attendee} ->
notify_parent({:saved, attendee})

{:noreply,
socket
|> put_flash(:info, "Attendee updated successfully")
|> push_patch(to: socket.assigns.patch)}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, changeset)}
end
end

defp save_attendee(socket, :new, attendee_params) do
case Accounts.create_attendee(attendee_params) do
{:ok, attendee} ->
notify_parent({:saved, attendee})

{:noreply,
socket
|> put_flash(:info, "Attendee created successfully")
|> push_patch(to: socket.assigns.patch)}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, changeset)}
end
end

defp assign_form(socket, %Ecto.Changeset{} = changeset) do
assign(socket, :form, to_form(changeset))
end

defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
end
47 changes: 47 additions & 0 deletions lib/fields_demo_web/live/attendee_live/index.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule FieldsDemoWeb.AttendeeLive.Index do
use FieldsDemoWeb, :live_view

alias FieldsDemo.Accounts
alias FieldsDemo.Accounts.Attendee

@impl true
def mount(_params, _session, socket) do
{:ok, stream(socket, :attendees, Accounts.list_attendees())}
end

@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end

defp apply_action(socket, :edit, %{"id" => id}) do
socket
|> assign(:page_title, "Edit Attendee")
|> assign(:attendee, Accounts.get_attendee!(id))
end

defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "New Attendee")
|> assign(:attendee, %Attendee{})

Check failure on line 26 in lib/fields_demo_web/live/attendee_live/index.ex

View workflow job for this annotation

GitHub Actions / Build and test

** (CompileError) lib/fields_demo_web/live/attendee_live/index.ex:26: FieldsDemo.Accounts.Attendee.__struct__/1 is undefined, cannot expand struct FieldsDemo.Accounts.Attendee. Make sure the struct name is correct. If the struct name exists and is correct but it still cannot be found, you likely have cyclic module usage in your code
end

defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Listing Attendees")
|> assign(:attendee, nil)
end

@impl true
def handle_info({FieldsDemoWeb.AttendeeLive.FormComponent, {:saved, attendee}}, socket) do
{:noreply, stream_insert(socket, :attendees, attendee)}
end

@impl true
def handle_event("delete", %{"id" => id}, socket) do
attendee = Accounts.get_attendee!(id)
{:ok, _} = Accounts.delete_attendee(attendee)

{:noreply, stream_delete(socket, :attendees, attendee)}
end
end
43 changes: 43 additions & 0 deletions lib/fields_demo_web/live/attendee_live/index.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<.header>
Listing Attendees
<:actions>
<.link patch={~p"/attendees/new"}>
<.button>New Attendee</.button>
</.link>
</:actions>
</.header>

<.table
id="attendees"
rows={@streams.attendees}
row_click={fn {_id, attendee} -> JS.navigate(~p"/attendees/#{attendee}") end}
>
<:col :let={{_id, attendee}} label="First name"><%= attendee.first_name %></:col>
<:col :let={{_id, attendee}} label="Last name"><%= attendee.last_name %></:col>
<:col :let={{_id, attendee}} label="Email"><%= attendee.email %></:col>
<:action :let={{_id, attendee}}>
<div class="sr-only">
<.link navigate={~p"/attendees/#{attendee}"}>Show</.link>
</div>
<.link patch={~p"/attendees/#{attendee}/edit"}>Edit</.link>
</:action>
<:action :let={{id, attendee}}>
<.link
phx-click={JS.push("delete", value: %{id: attendee.id}) |> hide("##{id}")}
data-confirm="Are you sure?"
>
Delete
</.link>
</:action>
</.table>

<.modal :if={@live_action in [:new, :edit]} id="attendee-modal" show on_cancel={JS.patch(~p"/attendees")}>
<.live_component
module={FieldsDemoWeb.AttendeeLive.FormComponent}
id={@attendee.id || :new}
title={@page_title}
action={@live_action}
attendee={@attendee}
patch={~p"/attendees"}
/>
</.modal>
21 changes: 21 additions & 0 deletions lib/fields_demo_web/live/attendee_live/show.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule FieldsDemoWeb.AttendeeLive.Show do
use FieldsDemoWeb, :live_view

alias FieldsDemo.Accounts

@impl true
def mount(_params, _session, socket) do
{:ok, socket}
end

@impl true
def handle_params(%{"id" => id}, _, socket) do
{:noreply,
socket
|> assign(:page_title, page_title(socket.assigns.live_action))
|> assign(:attendee, Accounts.get_attendee!(id))}
end

defp page_title(:show), do: "Show Attendee"
defp page_title(:edit), do: "Edit Attendee"
end
28 changes: 28 additions & 0 deletions lib/fields_demo_web/live/attendee_live/show.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<.header>
Attendee <%= @attendee.id %>
<:subtitle>This is a attendee record from your database.</:subtitle>
<:actions>
<.link patch={~p"/attendees/#{@attendee}/show/edit"} phx-click={JS.push_focus()}>
<.button>Edit attendee</.button>
</.link>
</:actions>
</.header>

<.list>
<:item title="First name"><%= @attendee.first_name %></:item>
<:item title="Last name"><%= @attendee.last_name %></:item>
<:item title="Email"><%= @attendee.email %></:item>
</.list>

<.back navigate={~p"/attendees"}>Back to attendees</.back>

<.modal :if={@live_action == :edit} id="attendee-modal" show on_cancel={JS.patch(~p"/attendees/#{@attendee}")}>
<.live_component
module={FieldsDemoWeb.AttendeeLive.FormComponent}
id={@attendee.id}
title={@page_title}
action={@live_action}
attendee={@attendee}
patch={~p"/attendees/#{@attendee}"}
/>
</.modal>
Loading

0 comments on commit 4701c18

Please sign in to comment.