When I call put_flash on my socket, it shows flash messages in the regular area inside of live.html.heex. It does not show flash messages in the modal when I call put_flash with a modal showing. In the book I’m reading, the modal shows as looking like this:
Have there been some changes that removed flash messages from the default live modal? How can I get mine to look more like the book? I’m using the default .modal
component.
defp handle_progress(:image, entry, socket) do
if entry.done? do
path =
consume_uploaded_entry(
socket,
entry,
&upload_static_file(&1, socket)
)
{:noreply,
socket
|> put_flash(:info, "file #{entry.client_name} uploaded")
|> assign(:image_upload, path)}
else
{:noreply, socket}
end
end
2 Likes
The modal was changed from a LiveComponent
to a function component in 1.6.3
and function components don’t use a separate @flash
assign on put_flash/3
.
Using something like
{:noreply,
socket
|> assign(:flash, %{"info" => "file #{entry.client_name} uploaded"})
|> assign(:image_upload, path)}
and then retrieving it in the template would probably be the easiest solution.
Under the hood the put_flash/3
/ get_flash/2
calls do a similar thing.
1 Like
I’ve ended up here after a few hours of failing to get my flashes to display in the modal.
Your idea gives me the error:
flash is a reserved assign by LiveView and it cannot be set directly. Use the appropriate flash functions instead.
Maybe phoenix has changed.
Something like this works in a liveview “controller”
socket =
socket
|> put_flash(:error, "NONONONON ")
{:noreply, socket}
# see that has the flash attached
# socket: #Phoenix.LiveView.Socket<
# ...
# flash: %{"error" => "NONONONON "},
#>
How can I access it? The modal has only assigns
and flash
is on the socket.
def modal(assigns) do ...
I can’t find a Phoenix.LiveView.get_flash()
, but even the regular get_flash
needs the socket
which the modal does not have. What is get_flash/2
anyway?
The docs now say
This function is deprecated. get_flash/2 is deprecated. Use Phoenix.Flash.get(@flash, key) instead.
but they don’t link to the new version.
Did you include the :fetch_live_flash
plug that makes the @flash
assign available in your HEEx template?
Note: You must also place the Phoenix.LiveView.Router.fetch_live_flash/2
plug in your browser’s pipeline in place of fetch_flash
for LiveView flash messages be supported, for example:
import Phoenix.LiveView.Router
pipeline :browser do
...
plug :fetch_live_flash
end
source: Phoenix.LiveView.put_flash/3 docs
Regarding Phoenix.Flash.get/2
, you can see how it’s used in the .flash
and .flash_group
components from the default core_components.ex
file.
Thanks for the response! Yes I’ve already included that plug. That’s not the fix.
This is the modal that comes w/ the liveView generator so I’d assumed they would support flashes. Is there no support for modal flashes? I think the flash might be rendering behind the modal, like on the main page (not 100% sure). Actually, the markup is behind, but no rendering it happening.
Some issues:
- The form get submitted and goes to
handle_event
and save_user
and the flash message is there. The socket
is the full gamut.
- the template re-renders but the
socket
is a truncated version w/o all the proper fields.
- I try to explain below but it’s very hard to
# index.html.heex
# This the the modal with a form inside the modal. The main part of the page.
<.modal return_to={Routes.user_index_path(@socket, ...} >
# the form is rendered here
<.live_component
module={MyApp.UserLive.FormComponent}
id={@user.id || :new}~
title={@page_title}
action={@live_action}
user={@user}
return_to={...}
/>
</.modal>
# form_component.ex
# on submit it is sent here
def handle_event("save", %{"user" => user_params}, socket) do
save_user(socket, socket.assigns.action, user_params)
defp save_user(socket, :new, user_params) do
case create_user(user_params) do
...
# we're in here now
{:error, %Ecto.Changeset{} = changeset} ->
socket =
socket
|> put_flash(:error, "TESTTEST123")
|> assign(:socket, socket)
# the flash message DOES exist here
IO.inspect(socket, label: "socket")
#socket: #Phoenix.LiveView.Socket<
# id: "phx-F2dreke_buifrTHn",
# ...
# flash: %{"error" => "TESTTEST123"},
# dozens of other fields
# >
{:noreply, assign(socket, :changeset, changeset)}
end
end
#form_component.html.heex - this is the <.live_component in the modal above
# This should re-render now with the the new socket, but it does not have the full socket.
<.form
let={f}
for={@changeset}
id="user-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save">
<% IO.inspect(@socket, label: "IN FORM") %>
# Phoenix.LiveView.Socket<
# id: "phx-F2dsD34IwIs3MGZh",
# ...
# assigns: #Phoenix.LiveView.Socket.AssignsNotInSocket<>,
# transport_pid: #PID<0.11640.0>,
# >
The socket picked up by the template is different and missing most of the fields.
I’m making sure to save the socket but for some reason it’s not persisting. Also, the modal doesn’t appear to even be setup for handle flash messages. Those are only on the parent behind.
Since it seems like you’re calling put_flash/3
from within the form LiveComponent and not calling push_navigate/2
or push_patch/2
, then that @flash
assign by put_flash/3
will only be available within that LiveComponent and will not be available in the parent LiveView meaning the .flash
and .flash_group
components at the layout level wouldn’t have anything to show.
Also from the put_flash/3
docs:
Note: While you can use put_flash/3
inside a Phoenix.LiveComponent
, components have their own @flash
assigns. The @flash
assign in a component is only copied to its parent LiveView if the component calls push_navigate/2
or push_patch/2
.
You should try adding a .flash
and/or .flash_group
component directly into the .modal
component e.g.
<.modal return_to={Routes.user_index_path(@socket, ...} >
<.flash_group flash={@flash} />
<.live_component
module={MyApp.UserLive.FormComponent}
...
/>
</.modal>
Regarding Phoenix.Flash.get/2
, you can see how it’s used in the .flash
and .flash_group
components from the default core_components.ex
file.
I don’t know what U mean by most of this, but I think I know why…
I don’t have a core_components.ex
. I found some docs about it but I don’t have any of the files talked about, even though I’ve used the generators. Do U know which generator create those? Googling this hasn’t worked out exactly (to find examples)
I read about those push functions also, but have no immediate idea how they work. I need to push some state from the child (where the flash occurs) to the parent. I guess that is how but at first use it is not working as expected. (Also undefined function push_navigate/2
)
The validation messages work as expected, weirdly. After “save” inside the child “controller,” the messages light up inside the form on the same level. But the logs (for testing) inside the form do not fire. So much here I don’t understand hahah.
What version of phoenix are you on, and what version of phoenix did you use to generate your project?
Thanks for the replies.
I managed to get it working. I still don’t fully understand why it works but essentially the modal form was just missing the flash markup. I added in the liveView flash markup and it started to work-ish.
.form
let={f}
for={@changeset}
id="user-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save">
<!-- this part I added -->
<main class="container">
<p class="alert alert-info" role="alert"
phx-click="lv:clear-flash"
phx-value-key="info"><%= live_flash(@flash, :info) %></p>
<p class="alert alert-danger" role="alert"
phx-click="lv:clear-flash"
phx-value-key="error"><%= live_flash(@flash, :error) %></p>
</main>
<%= label f, :first_name %>
<%= text_input f, :first_name %>
<%= error_tag f, :first_name %>
....
</.form>