Cat 5

  • Name
    Goose
  • Color
    Orange
  • Owner
    Wendy
  • Category
    Fictional

NOTE: reference the Setup section for generating the Animals context and adding the Cats to the database used in this example.

NOTE: while you can go through the process to create, edit, and delete cats in this preview, the actions have been disabled on the backend. The code below will still allow you to create, edit, and delete cats.

scope "/", LiveViewBasicsWeb do
  pipe_through :browser

  live "/cats", CatLive.Index, :index
  live "/cats/new", CatLive.Form, :new
  live "/cats/:id", CatLive.Show, :show
  live "/cats/:id/edit", CatLive.Form, :edit
end
defmodule LiveViewBasicsWeb.CatLive.Index do
  use LiveViewBasicsWeb, :live_view

  alias LiveViewBasics.Animals

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

  def handle_params(params, _uri, socket) do
    socket =
      socket
      |> stream(:cats, Animals.filter_cats(params), reset: true)
      |> assign(:form, to_form(params))

    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash}>
      <.header>
        Listing Cats
        <:actions>
          <.button navigate={~p"/cats/new"}>
            <.icon name="hero-plus" /> New Cat
          </.button>
        </:actions>
      </.header>
      <div class="mb-2">
        <.form
          class="sm:flex justify-left gap-4 items-center"
          for={@form}
          id="filter-form"
          phx-change="filter"
          phx-submit="filter"
        >
          <.input field={@form[:q]} placeholder="Search..." autocomplete="off" phx-debounce="500" />

          <.input
            type="select"
            field={@form[:category]}
            prompt="Category"
            options={["Real", "Fictional"]}
          />

          <.input
            type="select"
            field={@form[:sort_by]}
            prompt="Sort By"
            options={["Name", "Color", "Owner"]}
          />

          <.link class="mb-2" patch={~p"/cats"}>
            Reset
          </.link>
        </.form>

        <.table
          id="cats"
          rows={@streams.cats}
          row_click={fn {_id, cat} -> JS.navigate(~p"/cats/#{cat}") end}
        >
          <:col :let={{_id, cat}} label="Name">{cat.name}</:col>
          <:col :let={{_id, cat}} label="Color">{cat.color}</:col>
          <:col :let={{_id, cat}} label="Owner">{cat.owner}</:col>
          <:col :let={{_id, cat}} label="Category">{cat.category}</:col>
          <:action :let={{_id, cat}}>
            <div class="sr-only">
              <.link navigate={~p"/cats/#{cat}"}>Show</.link>
            </div>
            <.link navigate={~p"/cats/#{cat}/edit"}>Edit</.link>
          </:action>
          <:action :let={{id, cat}}>
            <.link
              phx-click={JS.push("delete", value: %{id: cat.id}) |> hide("##{id}")}
              data-confirm="Are you sure?"
            >
              Delete
            </.link>
          </:action>
        </.table>
      </div>
    <Layouts.app flash={@flash}>
    """
  end

  def handle_event("filter", params, socket) do
    params =
      params
      |> Map.take(~w(q category sort_by))
      |> Map.reject(fn {_, v} -> v == "" end)

    socket = push_patch(socket, to: ~p"/cats?#{params}")

    {:noreply, socket}
  end

  def handle_event("delete", %{"id" => id}, socket) do
    cat = Animals.get_cat!(id)
    {:ok, _} = Animals.delete_cat(cat)

    {:noreply, stream_delete(socket, :cats, cat)}
  end
end
defmodule LiveViewBasicsWeb.CatLive.Show do
  use LiveViewBasicsWeb, :live_view

  alias LiveViewBasics.Animals

  def mount(%{"id" => id}, _session, socket) do
    socket = assign(socket, :cat, Animals.get_cat!(id))

    {:ok, socket}
  end

  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash}>
      <.header>
        Cat {@cat.id}
        <:actions>
          <.button navigate={~p"/cats/#{@cat}/edit"}>
            <.icon name="hero-pencil-square" /> Edit Cat
          </.button>
        </:actions>
      </.header>
      <div class="mb-2">
        <.list>
          <:item title="Name">{@cat.name}</:item>
          <:item title="Color">{@cat.color}</:item>
          <:item title="Owner">{@cat.owner}</:item>
          <:item title="Owner">{@cat.category}</:item>
        </.list>
      </div>
    <Layouts.app flash={@flash}>
    """
  end
end
defmodule LiveViewBasicsWeb.CatLive.Form do
  use LiveViewBasicsWeb, :live_view

  alias LiveViewBasics.Animals
  alias LiveViewBasics.Animals.Cat

  def mount(params, _sessions, socket) do
    socket = apply_action(socket, socket.assigns.live_action, params)
    {:ok, socket}
  end

  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash}>
      <.header>
        Cat Form
      </.header>

      <div class="mb-2">
        <.form for={@form} id="cat-form" phx-change="validate" phx-submit="save">
          <.input field={@form[:name]} type="text" label="Name" />
          <.input field={@form[:color]} type="text" label="Color" />
          <.input field={@form[:owner]} type="text" label="Owner" />
          <.input field={@form[:category]} type="text" label="Category" />
          <footer>
            <.button phx-disable-with="Saving...">Save Cat</.button>
          </footer>
        </.form>
      </div>
    <Layouts.app flash={@flash}>
    """
  end

  def handle_event("validate", %{"cat" => cat_params}, socket) do
    changeset = Animals.change_cat(socket.assigns.cat, cat_params)
    {:noreply, assign(socket, form: to_form(changeset, action: :validate))}
  end

  def handle_event("save", %{"cat" => cat_params}, socket) do
    save_cat(socket, socket.assigns.live_action, cat_params)
  end

  defp apply_action(socket, :new, _params) do
    cat = %Cat{}

    socket
    |> assign(:cat, cat)
    |> assign(:form, to_form(Animals.change_cat(cat)))
  end

  defp apply_action(socket, :edit, %{"id" => id}) do
    cat = Animals.get_cat!(id)

    socket
    |> assign(:cat, cat)
    |> assign(:form, to_form(Animals.change_cat(cat)))
  end

  defp save_cat(socket, :new, cat_params) do
    case Animals.create_cat(cat_params) do
      {:ok, cat} ->
        socket =
          socket
          |> put_flash(:info, "Cat created successfully")
          |> push_navigate(to: ~p"/cats/#{cat}")

        {:noreply, socket}

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

  defp save_cat(socket, :edit, cat_params) do
    case Animals.update_cat(socket.assigns.cat, cat_params) do
      {:ok, cat} ->
        socket =
          socket
          |> put_flash(:info, "Cat updated successfully")
          |> push_navigate(to: ~p"/cats/#{cat}")

        {:noreply, socket}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, form: to_form(changeset))}
    end
  end
end
def filter_cats(filter) do
  Cat
  |> where([c], ilike(c.name, ^"%#{filter["q"]}%"))
  |> with_category(filter["category"])
  |> sort(filter["sort_by"])
  |> Repo.all()
end

defp with_category(query, category) when category in ~w(Real Fictional) do
  where(query, category: ^category)
end

defp with_category(query, _), do: query

defp sort(query, "Name"), do: order_by(query, :name)
defp sort(query, "Color"), do: order_by(query, :color)
defp sort(query, "Owner"), do: order_by(query, :owner)
defp sort(query, _), do: order_by(query, :id)

# The code below this point already exists if you ran the context generator in the Setup
def create_cat(attrs) do
  %Cat{}
  |> Cat.changeset(attrs)
  |> Repo.insert()
end

def update_cat(%Cat{} = cat, attrs) do
  cat
  |> Cat.changeset(attrs)
  |> Repo.update()
end

def delete_cat(%Cat{} = cat) do
  Repo.delete(cat)
end