Compare Phoenix 1.7 LiveView generator with 1.6
The official release note has more detail which I strongly suggest reading. But I sort out some changes from Phoenix version 1.6 LiveView generator to 1.7 if you want a direct comparison for a glance.
Code shows in this post are generated by this command in brand-new phoenix projects.
mix phx.gen.live Posts Post posts title content:text
Before the generator, let’s check core_compoents first.
-
No more
live_helpers.ex. In 1.6, if we call live generator for the first time in the project, it will generate alive_helpers.exfile. It contains a modal component for our generated CRUD. In 1.7, all components we need already exist incore_components.exnow. -
Since version 1.7 has TailwindCSS built in. All the code we generated and components in the
core_compoents.exfile are styled with Tailwind now. -
All functions in
Phoenix.HTML.Formhave been moved to eithercore_componentsorPhoenix.Components
Index
In the index module, the only difference is we now use Phoenix.LiveView.stream/4 instead of assigning the list directly.
mount
def mount(_params, _session, socket) do
- {:ok, assign(socket, :posts, list_posts())}
+ {:ok, stream(socket, :posts, Posts.list_posts())}
end
handle_event(“delete”
{:ok, _} = Posts.delete_post(post)
- {:noreply, assign(socket, :posts, list_posts())}
+ {:noreply, stream_delete(socket, :posts, post)}
There’s a new handle_info in this file. We’ll address that in the FormComponent section later.
Index template
In the index.html.heex file, the new version leverage the new table component.
+ <.table
+ id="posts"
+ rows={@streams.posts}
+ row_click={fn {_id, post} -> JS.navigate(~p"/posts/#{post}") end}
+ >
+ <:col :let={{_id, post}} label="Title"><%= post.title %></:col>
+ <:col :let={{_id, post}} label="Content"><%= post.content %></:col>
+ <:action :let={{_id, post}}>
+ <div class="sr-only">
+ <.link navigate={~p"/posts/#{post}"}>Show</.link>
+ </div>
+ <.link patch={~p"/posts/#{post}/edit"}>Edit</.link>
+ </:action>
+ <:action :let={{id, post}}>
+ <.link
+ phx-click={JS.push("delete", value: %{id: post.id}) |> hide("##{id}")}
+ data-confirm="Are you sure?"
+ >
+ Delete
+ </.link>
+ </:action>
+ </.table>
Show
No change here 😀
Show template
The show.html.heex file is similar to the index template. Besides replacing the plane template with new components, there’s no other change.
+ <.list>
+ <:item title="Title"><%= @post.title %></:item>
+ <:item title="Content"><%= @post.content %></:item>
+ </.list>
FormComponent
After creating a new item or updating an existing item in this live_compoennt, the old strategy is push_redirect to return_to after we make the change.
socket
|> put_flash(:info, "Post updated successfully")
- |> push_redirect(to: socket.assigns.return_to)}
+ |> push_patch(to: socket.assigns.patch)}
In the new version, We notify parent LiveView about the new change. Then push_patch to patch(used to be return_to) instead of push_redirect. There are two kinds of outcomes depending on what the parent LiveView is.
-
Inside Index: The new
handle_infoin Index willstream_insertthe new or changed item without updating the whole list. Then handle_params triggered by push_path updated the title and reset the post to nil. -
Inside Show: There’s no
handle_info. We only trigger thehandle_paramsbypush_path.handle_paramsthen update the title and current item.
- {:ok, _post} ->
+ {:ok, post} ->
+ notify_parent({:saved, post})
+ defp notify_parent(msg) do
+ send(self(), {__MODULE__, msg})
+ end
Also, in the new version, We assign form(made by to_form(changeset)) instead of a changeset because of the recent form component change.
- |> assign(:changeset, changeset)}
+ |> assign_form(changeset)}
+ defp assign_form(socket, %Ecto.Changeset{} = changeset) do
+ assign(socket, :form, to_form(changeset))
+ end
FormComponent Template
No form_component.html.heex now. Thanks to the new compact sinple_form component, the template size is now small enough to be in the render function.
+ <.simple_form
+ for={@form}
+ id="post-form"
+ phx-target={@myself}
+ phx-change="validate"
+ phx-submit="save"
+ >
+ <.input field={@form[:title]} type="text" label="Title" />
+ <.input field={@form[:content]} type="text" label="Content" />
+ <:actions>
+ <.button phx-disable-with="Saving...">Save Post</.button>
+ </:actions>
+ </.simple_form>