I am stumped on an issue I am seeing with Phoenix LiveView. I have the following phoenix component:
attr :user, MyApp.User, required: true
def my_component(assigns) do
~H"""
<h1> Hi, <%= @user.first_name %> </h1>
"""
end
and I invoke it passing in my Ecto User struct like so:
defmodule MyApp.MyLive do
use MyAppWeb, :live_view
def render(assigns) do
~H"""
<div>
<.my_component user={Repo.get!(User, 1) />
</div>
"""
end
end
Now when I try to render this live view I get the error protocol Phoenix.HTML.Safe not implemented for %MyApp.User{} ...
.
This leads me to two questions:
- Why does my struct need to implement the Safe protocol
- The LiveView docs for
attr
state: Required struct types are annotated and emit compilation warnings. For example, if you specify attr :user, User, required: true and then you write @user.non_valid_field in your template, a warning will be emitted.
This seems at least lacking additional instruction if you can’t do that without implementing the Safe protocol first.
You need a .
to invoke function components:
<.my_component user={Repo.get!(User, 1)} />
You probably want to assign the user outside of render
and pass that assign instead because functions in templates do not play well with change tracking.
If your my_component
definition lives in a module that is not imported, you must fully qualify it with:
<MyModule.my_component user={@user} />
2 Likes
Also this is not the correct syntax, it should be…
<h1> Hi, <%= @user.first_name %></h1>
1 Like
Good catch. I updated the code samples above. And I have the components being imported. Questions 1 & 2 still stand.
That Phoenix.HTML.Safe
error is just what you get when you try and cast a value into HTML. If your original examples were how you were doing it, it’s just because HEEx was trying to cast the literal struct %User{}
into valid HTML (essentially just a string) which it couldn’t. You would get the same error if you did something like:
~H"<%= %Ecto.Multi{} %>"
So if you initially did have <my_component user={Repo.get!(User, 1) />
without the .
, then the user={}
was trying to cast a %User{}
into a valid HTML string. You could implement the protocol for these structs if you wanted to… it’s basically the same as to_string
methods in OO, but there is no need to and I wouldn’t really recommend it.
Thanks soda! Sorry I should have been more clear. I coded up a sample that captured my use case and just forgot the .
in that, not in my actual project. That makes sense what Phoenix.HTML.Safe
is now. I still don’t really understand why the user struct is being cast to HTML. I can start a brand new LiveView project and put it on git if that would be helpful.
oh my god. Upon looking at my code again I realized I did, in fact, leave off the .
on my LiveView Component. Once I fixed that it worked. Thank you!!
1 Like