How to create HTML inside function and insert it to template?

Hi,

I want to create some HTML inside view function and then insert it to template, how do I do that? In this case, I want to calculate percentage from project completed/project goal. I’m not using LiveView here and this is a simplified code, also, I have to create HTML this way, I can’t avoid it.

view.ex

def calculate_progress(goal, completed) do
    progress = completed / goal * 100 |> round()

    # How to create HTML which can be added to template?
    <div>#{progress}%</div>
end

template.html.heex

<div>
    <%= calculate_progress(@project.goal, @project.completed) %>
</div>

You can use Phoenix.Component — Phoenix LiveView v0.18.3

view.ex

attr :completed, :integer, required: true
attr :progress, :integer, required: true

def progress(assigns) do
  progress = assigns.completed / assigns.goal * 100 |> round()
  assigns = assign(assigns, :progress, progress)
  
  ~H"""
  <div>@progress</div>
  """
end

template.html.heex

<div>
  <.progress goal={@project.goal} completed={@project.completed} />
</div>
1 Like

I’m getting undefined function sigil_H/2 error, probably because I’m not using LiveView.

Try adding use Phoenix.Component at the top of your view. This should work for non-liveviews as well

Thanks for helping me out here but I’m getting a whole bunch of errors all around… is there a way to do it without LiveView?

You can use Phoenix.HTML.Tag — Phoenix.HTML v3.2.0

def calculate_progress(goal, completed) do
  progress = completed / goal * 100 |> round()
  content_tag(:div, progress)
end
1 Like

Yes, this works for single tags as I wrote in the example but that was just a simplified code… Is there a way to create a whole HTML block like this?

<div class="div-class">
  <div class="some-class">PROGRESS FUNCTION %</div>
  <p="p-class">some text here</p>
  <p="another-p-class">another text here</p>
</div>

This should** work (not tested), might have to put the inner tags in a list

content_tag :div, [class: "div-class"] do
  content_tag(:div, [class: "some-class"] do
    progress
  end
  content_tag(:p, [class: "p-class"] do
    "some text here"
  end
  ...
end
1 Like

It worked, thanks a lot for your help!