phoenix_live_view
phoenix_live_view copied to clipboard
"lv:clear-flash" does not clear flash message in LiveComponent
Environment
- Elixir version (elixir -v): 1.13.1
- Phoenix version (mix deps): 1.6.6
- Phoenix LiveView version (mix deps): 0.17.6
- Operating system: Mac
- Browsers you attempted to reproduce this bug on (the more the merrier): Chrome
- Does the problem persist after removing "assets/node_modules" and trying again? Yes/no: Yes
Actual behavior
If a flash message is set in a LiveComponent, "lv:clear-flash" effectively clears the flash message from the parent LiveView but the flash message assign in the child component is not cleared. I am using the phx-value-key of "error".
Expected behavior
Right now I'm manually clearing the flash message in the LiveComponent but I would have expected "lv:clear-flash" to reset the flash messages in the LiveView and the LiveComponent that originated the flash message.
Hi @mtrach2020, are you setting phx-target
so the lv:clear-flash instruction targets the component?
Hi @mtrach2020, are you setting
phx-target
so the lv:clear-flash instruction targets the component?
Thanks for the quick response!
That clears the flash message in the LiveComponent but not the parent LiveView (where the error messages are actually displayed through the live.html.heex layout). Per the docs, I'm using put_flash in the LiveComponent and then patching the LiveView, which updates the flash message in my live.html.heex template.
This all makes sense to me in terms of the way LiveView works (the docs are great at explaining how to track state when there are LiveComponents).
I guess there's something about flash messages that feels like a "special case", though. If using put_flash in a component updates the flash message in that compoenent AND the parent (after patching/redirecting), it felt to me like lv:clear-flash would do the opposite (clear flash message in parent and children).
It feels like it would be a common pattern to set a flash message in a LiveComponent but have that message displayed by the live.html.heex template, so I'm not sure what your thoughts are on best practices for handling this.
Here's the specific use case that may make this more clear:
- User enters invalid data in LiveComponent form
- I use put_flash to set error
- Error message is displayed through live.html.heex
- User clicks "x" on error message to attempt to close the window
- Then user enters valid data and submits it
If the "x" targets the LiveComponent, the LiveComponent flash message is cleared but the live.html.heex template is still displaying the error message (presumably because the parent LiveView still has the same message in its assigns).
If the "x" has no target, it clears the parent flash message, so the warning message disappears but the child still has that message in assigns, so when the user submits the valid data, they still get the same error message from before.
So for now, I'm just setting the flash message to nil every time valid data is submitted. This is a very simple fix but I'm just bringing this up because it feels messy.
Another options that comes to mind is to do (assuming the flash message is displayed in a div with ID "flash-error"):
phx-target={"#flash-error, [... list of every LiveComponent that has the potential for creating a flash error ...]"}
Thanks for the write up, I understand the issue now. The only thing is that I would expect the child flash to be erased when we copy the flash to the parent, so there is something else at play here?
Yeah, makes sense that it would completely solve the problem to clear after copying it but at least that's not what's happenning for myself.
For more context, this is the code in my LiveComponent:
def handle_event("create_section", %{"section" => section_params}, socket) do
changeset =
%Section{}
|> Changeset.cast(section_params, [:type, :name])
|> Changeset.put_change(:studio_id, Repo.get_studio_id())
|> Changeset.unsafe_validate_unique([:name, :studio_id], Repo,
message: "Your website already has a section with that name."
)
socket =
if changeset.valid? do
section = Section.create_section!(changeset)
socket
# This is in case there was a flash error still in memory from a previous submit (lv:clear doesn't work)
|> put_flash(:error, nil)
|> push_redirect(
to: Routes.live_path(socket, Admin.WebsiteLive, section: section.id)
)
else
socket
|> assign(:changeset, changeset)
|> put_flash(:error, LayoutView.parse_changeset_errors(changeset))
|> push_patch(
to: Routes.live_path(socket, Admin.WebsiteLive, section: "new")
)
end
{:noreply, socket}
end
This is from my live.html.heex:
<button
class="bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
phx-click="lv:clear-flash" phx-value-key="error">
<span class="sr-only">Close</span>
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
Hi @mtrach2020, are you setting
phx-target
so the lv:clear-flash instruction targets the component?
Related to this observation, the assertion https://github.com/phoenixframework/phoenix_live_view/blob/405f376b90d8df3b3242fabb6d7fe2c422c0a2e9/test/phoenix_live_view/integrations/flash_test.exs#L367 is causing a false positive because of https://github.com/phoenixframework/phoenix_live_view/blob/405f376b90d8df3b3242fabb6d7fe2c422c0a2e9/test/support/live_views/flash.ex#L87
To replicate change the assert to:
assert has_element?(flash_live, "span[phx-value-key=info]", "component[]:info")
Which is caused by the fact that the test lv component has no phx-target
Hi guys, probably not related to this 100% however this sent me through a rabbit hole and i found this:
.alert:empty {
display: none;
}
What's the purpose of that? I noticed that the p dom element is not removed. So when I tried to add say SVG cross icon, I noticed the flash is always there. I started researching (this is how i found this) and then boom I noticed the CSS. I guess the reason behind "keep the p dom element" always there and just render the text within (and when it's empty just display-none-ed) is performance? That's my question: what's the logic behind?
Thanks!
@thelastinuit https://github.com/phoenixframework/phoenix/pull/4667 is gonna fix the issue you reported for good
I've stumbled upon something similar. In the same live component, phx-click="lv:clear-flash"
clears the flash set by the liveview, but phx-window-keydown="lv:clear-flash" phx-key="escape"
does not.
If, instead, I manually handle the event (clear-flash
) in the live view, the flash gets cleared:
<div phx-window-keydown="clear-flash" phx-key="escape">
def handle_event("clear-flash", _params, socket) do
{:noreply, clear_flash(socket)}
end
Is "lv:clear-flash"
only supposed to work with phx-click
?