Hi!
Sorry, I am very new to LiveView and Phoenix, and can’t figure out what is going on.
And I see in Network tab/WS that diff result that is coming from the server is sending diff not only for email input which was the only one to be changed, but for password field as well and even for dropdown I have in header which is present in layouts/app.html.heex
.
Could you please help to to figure this put?
I have a liveview like:
defmodule AppWeb.TestFormLive do
use AppWeb, :live_view
alias App.Accounts
import Ecto.Changeset
def render(assigns) do
~H"""
<.simple_form
id="asd"
for={@form}
phx-change={JS.push("validate", loading: "#asd")}
phx-submit="save"
>
<.text_field field={@form[:email]} type="email" label="Email" required />
<.text_field field={@form[:password]} type="password" label="Password" required />
<.button phx-disable-with="Creating account..." class="w-full">Create an account</.button>
</.simple_form>
"""
end
def mount(_params, _session, socket) do
changeset = Ecto.Changeset.cast(%Accounts.User{}, %{}, [])
form = to_form(changeset, as: "user")
socket = assign(socket, form: form)
{:ok, socket}
end
def handle_event("inc_temperature", _params, socket) do
{:noreply, update(socket, :temperature, &(&1 + 1))}
end
def handle_event("validate", %{"user" => attrs}, socket) do
changeset =
%Accounts.User{}
|> cast(attrs, [:email, :password])
|> validate_length(:email, max: 1)
form = to_form(Map.put(changeset, :action, :validate), as: "user")
{:noreply, assign(socket, form: form)}
end
def handle_event("save", _params, socket) do
{:noreply, socket}
end
end
Here is app.html.heex
which is used for live and dead views:
<.app_header {assigns} />
<div class="grid items-start grid-cols-6 gap-12 mx-auto mt-10 max-w-screen-2xl">
<.app_sidebar />
<main class="col-span-4 xl:col-span-5">
<%= @inner_content %>
</main>
</div>
App header looks like:
<header id="app-header" class="bg-white border-b border-gray-100">
<div class="px-2 mx-auto max-w-screen-2xl sm:px-6 lg:px-8">
<div class="relative flex justify-between h-16">
<div class="flex items-stretch justify-start flex-1">
<div class="flex items-center flex-shrink-0">
<.link_to href={~p"/"}>
<img
class="w-auto h-12"
src="https://cdn-icons-png.flaticon.com/512/4119/4119945.png"
alt="Your Company"
/>
</.link_to>
</div>
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
<%= if @current_user do %>
<.dropdown id="profile-dropdown">
<:button>
<img
class="w-8 h-8 rounded-full mr-2"
src="https://cdn-icons-png.flaticon.com/512/4119/4119945.png"
/>
<%= @current_user.email %>
</:button>
<.dropdown_item href={~p"/"}>Your profile</.dropdown_item>
<.dropdown_item href={~p"/users/settings"}>
<%= dgettext("accounts", "Account settings") %>
</.dropdown_item>
<.dropdown_divider />
<.dropdown_item href={~p"/users/sign_out"} method="delete">
<%= dgettext("accounts", "Sign out") %>
</.dropdown_item>
</.dropdown>
<% else %>
<.button variant="flat" class="mr-2" href={~p"/users/sign_up"}>
<%= dgettext("accounts", "Sign up") %>
</.button>
<.button variant="success" href={~p"/users/sign_in"}>
<%= dgettext("accounts", "Sign in") %>
</.button>
<% end %>
</div>
</div>
</div>
</header>
And dropdown component:
defmodule AppWeb.Components.UI.Dropdown do
import AppWeb.Components.UI.Button
use Phoenix.Component
alias Phoenix.LiveView.JS
attr :id, :string, required: true
slot :button, required: true
slot :inner_block, required: true
def dropdown(assigns) do
~H"""
<div id={@id}>
<%= for btn <- @button do %>
<.toggle_button id={"#{@id}-toggle-button"} {btn}><%= render_slot(btn) %></.toggle_button>
<% end %>
<.dropdown_content id={"#{@id}-toggle-button"}>
<%= render_slot(@inner_block) %>
</.dropdown_content>
</div>
"""
end
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def dropdown_item(assigns) do
~H"""
<.button variant="flat" class={["ui--dropdown--menu-item--tag", @class]} {@rest}>
<%= render_slot(@inner_block) %>
</.button>
"""
end
def dropdown_divider(assigns) do
~H"""
<div class="ui--dropdown--menu-item--divider"></div>
"""
end
attr :id, :string, required: true
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
defp toggle_button(assigns) do
~H"""
<.button
id={@id}
variant="flat"
class={["rounded-xl peer", @class]}
data-state="closed"
phx-click={toggle()}
phx-click-away={hide()}
{@rest}
>
<%= render_slot(@inner_block) %>
</.button>
"""
end
attr :id, :string, required: true
attr :class, :string, default: nil
slot :inner_block, required: true
defp dropdown_content(assigns) do
~H"""
<div
id={@id}
class={[
"absolute peer-data-[state=closed]:hidden z-20 right-0 p-1 mt-2 origin-top-right bg-white border border-gray-200 rounded-xl focus:outline-none",
@class
]}
>
<%= render_slot(@inner_block) %>
</div>
"""
end
defp toggle(js \\ %JS{}) do
JS.toggle_attribute(js, {"data-state", "open", "closed"})
end
defp hide(js \\ %JS{}) do
JS.set_attribute(js, {"data-state", "closed"})
end
end