I’m currently going through Pragmatic Studio’s LiveView 0.15 course using Phoenix 1.7.7. and I’m struggle to convert the following component to a HEEx component. I’ll tell you right now, I have no shame in my game so I’m leading with action and posting toxic code below (the second code block). I’ve looked all over and can’t seem to find how to set phx-target={@myself}. Needless to say, currently it always returns an ArgumentError “assign @myself not available in template.” In a week, hopefully I’ll look back at this and laugh, but folks, I’m struggling with this one. I looked through the docs and couldn’t find it, now I’m thinking it’s gone on to tunnel vision.
defmodule LiveViewStudioWeb.DeliveryChargeComponent do
use LiveViewStudioWeb, :live_component
alias LiveViewStudioWeb.SandboxCalculator
import Number.Currency
def mount(socket) do
{:ok, assign(socket, zip: nil, charge: 0)}
end
def render(assigns) do
~L"""
<form phx-change="calculate"
**phx-target="<%= @myself %>">**
<div class="field">
<label for="zip">Zip Code:</label>
<input type="text" name="zip" value="<%= @zip %>" />
<span class="unit"><%= number_to_currency(@charge) %></span>
</div>
</form>
"""
end
def handle_event("calculate", %{"zip" => zip}, socket) do
charge = SandboxCalculator.calculate_delivery_charge(zip)
socket = assign(socket, zip: zip, charge: charge)
send(self(), {:delivery_charge, charge})
{:noreply, socket}
end
end
This is what I’ve been able to compile
defmodule LiveViewStudioWeb.DeliveryChargeComponent do
use Phoenix.Component
import Number.Currency
alias LiveViewStudio.SandboxCalculator
def delivery_charge(assigns) do
# compiles but is definitely wrong
assigns = assign_new(assigns, :charge, fn -> 0 end)
assigns = assign_new(assigns, :zip, fn -> nil end)
~H"""
<div>
<form phx-change="calculate" >
<div class="field">
<label for="zip">Zip Code:</label>
<input type="text" name="zip" value={@zip} />
<span class="unit"><%= number_to_currency(@charge) %></span>
</div>
</form>
</div>
"""
end
def handle_event("calculate", %{"zip" => zip}, socket) do
charge = SandboxCalculator.calculate_delivery_charge(zip)
socket = assign(socket, zip: zip, charge: charge)
send(self(), {__MODULE__, :delivery_charge, charge})
{:noreply, socket}
end
end
What does the live_component/0 function in your LiveViewStudioWeb module look like? It should include use Phoenix.LiveComponent and look something like this:
def live_component do
quote do
use Phoenix.LiveComponent
unquote(html_helpers())
end
end
Appreciate the response. I didn’t realized I’d typed use Phoenix.Component, however, I did change it to use Phoenix.LiveComponent and the results were the same:
ArgumentError at GET /sandbox
assign @myself not available in template.
Please make sure all proper assigns have been set. If you are
calling a component, make sure you are passing all required
assigns as arguments.
Available assigns: [:zip, :__changed__, :charge]
I initially used a LEEx form with use LiveViewStudio, :live_component, mount/3 and render/1 as placed in the first code block. <form phx-change="calculate" phx-target="<%= @myself %>" had no issues.
I refactored to HEEx. The refactor included instantiating the component with the line<DeliveryChargeComponenet.delivery_charge zip={nil charge={0} />. In the component file, I did incorrectly use Phoenix.Component (and have since changed that to use Phoenix.LiveComponent) and removed mount/3 and render/1, and set <form> to <form phx-change="calculate" phx-target={@myself}>.
I’m just realizing I did not mention the console logs:
warning: render/1 was not implemented for LiveViewStudioWeb.DeliveryChargeComponent.
Make sure to either explicitly define a render/1 clause with a LiveView template:
It’s using HEEx, but I changed the function name anyway, from delivery_charge to render to remove that from the equation.
You’re calling the live component as if it were a function component. You need to use live_component, just like you’re doing with SandboxCalculatorComponent.