CRUD
My first LiveView project
Read time: 6 minutes
This section of the course is going to be very special. We will put into practice many things that we already talked about previously to create a very simple product management system. Basically a CRUD (Create-Read-Update-Delete).
To simulate a real project we will name our project Super Store. Our main module will be called SuperStore
and the product system will be SuperStore.Catalog
.
#Graduating from LiveView Playground
So far we used LiveView Playground because our codes were very simple and didn't need so many lines. Even our form in the previous class was less than 70 lines.
From now on we will put a lot of emphasis on organization. We will have multiple modules and repeating each one with small changes here and there could easily become distracting during our lesson.
That being said let's learn this time how to do all of this in a real Mix project. Remember that Mix is Elixir's compilation tool so we are finally working on a real project!
#Ecto with SQLite
For this specific CRUD we will use Ecto (the library that helps us work with databases) alongside SQLite3. We chose SQLite for two very simple reasons:
- Does not require installing anything extra on your computer: just install the library and the database can be started.
- What we will learn here is easily reusable in other databases: we will focus on fundamental Ecto operations so it doesn't matter if you intend to use PostgreSQL or MySQL in the future the code will be the same.
#Migrating our form to a starter Phoenix project
We will clone a base project that I prepared to ensure that everyone viewing this lesson has the same starting point. Using your terminal:
git clone https://github.com/adopt-liveview/first-crud.git
cd first-crud
mix setup
With these commands you should have a base Phoenix project. The mix setup
command not only installs things but also compiles the dependencies for you. Once you have prepared the project and opened your preferred code editor run mix phx.server
and go to http://localhost:4000 to see the home page of your Phoenix project.
Don't worry about exploring existing files as we'll cover them as needed.
#Creating the product.ex
file
Now our project is called SuperStore
so we need to make this part of the module that we currently call Product
. As mentioned previously, our product management system will be SuperStore.Catalog
so we will move Product
to SuperStore.Catalog.Product
.
In Phoenix we call modules that encapsulate functions that manage a part of our application Context Modules. The Catalog
Context is responsible for managing our products. If we had a Context called Accounts
it would be in charge of managing user accounts. Each Context can have zero or more schemas and generally the naming will be YourProject.YourContext
and YourProject.YourContext.YourSchema
.
Inside the lib/super_store
folder create a file called catalog.ex
with the following content:
defmodule SuperStore.Catalog do
end
Then create a folder lib/super_store/catalog
with a file named product.ex
with the following content:
defmodule SuperStore.Catalog.Product do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :name, :string, default: ""
field :description, :string, default: ""
end
def changeset(product, params \\ %{}) do
product
|> cast(params, [:name, :description])
|> validate_required([:name, :description])
end
end
#Adding our LiveView
Everything related to our web part of our code will be placed inside the lib/super_store_web
folder. Inside this folder create a folder called live
and add the file page_live.ex
with the following content:
defmodule SuperStoreWeb.PageLive do
use SuperStoreWeb, :live_view
import SuperStoreWeb.CoreComponents
alias SuperStore.Catalog.Product
def mount(_params, _session, socket) do
form =
Product.changeset(%Product{})
|> to_form()
{:ok, assign(socket, form: form)}
end
def handle_event("validate_product", %{"product" => product_params}, socket) do
form =
Product.changeset(%Product{}, product_params)
|> Map.put(:action, :validate)
|> to_form()
{:noreply, assign(socket, form: form)}
end
def handle_event("create_product", %{"product" => product_params}, socket) do
IO.inspect({"Form submitted!!", product_params})
{:noreply, socket}
end
def render(assigns) do
~H"""
<div class="bg-grey-100">
<.form
for={@form}
phx-change="validate_product"
phx-submit="create_product"
class="flex flex-col max-w-96 mx-auto bg-gray-100 p-24"
>
<h1>Creating a product</h1>
<.input field={@form[:name]} placeholder="Name" />
<.input field={@form[:description]} placeholder="Description" />
<.button type="submit">Send</.button>
</.form>
</div>
"""
end
end
The only changes here were to the module name and the macros at the top of the module. In this project our LiveViews will always start with SuperStoreWeb.
. We also changed the LiveviewPlaygroundWeb
references to SuperStoreWeb
and added an alias SuperStore.Catalog.Product
.
How does alias
actually work?
YourProject.YourContext.YourSchema
but if you put an alias YourProject.YourContext.YourSchema
you can just use the last module part like YourSchema.changeset()
. We need to make this alias here because our module now has a name with multiple parts, unlike before when it was simply called Product
.
As you can see our LiveView Playground is extremely close to what a LiveView of a real project should be.
#Changing the main route for our LiveView
Open the router.ex
file located in the lib/super_store_web
folder. You should notice that it has a single route that is not a LiveView set to get "/", PageController, :home
. Change this to live "/", PageLive, :home
.
#Project migrated!
From now on you should have the same project as the last lesson working with the only exception that you should see a navigation bar at the top.
If you had any issues you can see the final code for this lesson using git checkout my-first-liveview-project-done
or cloning it in another folder using git clone https://github.com/adopt-liveview/first-crud .git --branch my-first-liveview-project-done
.
#Recap!
-
Now that we are in a real project we will use the
.ex
extension instead of.exs
(used for scripts). -
Phoenix projects organize parts of their systems into modules called Context that are generally located in
lib/your_app
. -
Context Modules can have zero or more schemas living in
lib/your_app/your_context
. -
Everything related to the web part of your project will live in
lib/your_app_web
. -
All LiveViews will be at
lib/your_app_web/live
. -
Don't forget that to make a LiveView reachable we need to modify our
router.ex
.
Feedback
Got any feedback about this page? Let us know!