How to use embed heex inside heex? (Phoenix 1.7rc)

Hey guys :slight_smile:

Sorry if this question is dumb but I’m trying to give Phoenix 1.7rc a whirl and I simply can’t get this working.
I have created a new project using mix phx.new and also ran mix phx.gen.html Todo Item items text:string person_id:integer status:integer - this is just to create a Todo item.

My file directory now looks like this (I’ve omitted lots of files for simplicity sake):

app_web
  - components
  - controllers
     - item_html
          . edit.html.heex
          . index.html.heex
          . new.html.heex
          . show.html.heex
     - page_html
     . item_controller.ex
     . item_html.ex

What I wish to do

new.html.heex has a simple form. I just want to embed it in index.html.heex.
In previous versions, this was possible by using the following line in index.html.heex:

<%= render "new.html", Map.put(assigns, :action, Routes.item_path(@conn, :create)) %>

But in this new version of Phoenix 1.7rc, I simply can’t get this to work :slightly_frowning_face:

What I tried

I scoured the documentation but I can’t get this to work properly.

I tried another approach, as well.
The item_controller.ex has many pre-built functions (create, edit, show) which relate to different operations I can do on the created Todo model I previously generated. The new/2 function pertains to the function that handles whenever I want to create a new Todo (it uses the new.html.heex file as template).

  def new(conn, _params) do
    changeset = Todo.change_item(%Item{})
    render(conn, :new, changeset: changeset)
  end

image

Since the new.html.heex is used with this function, I thought that I could just use the following piece of code inside index.html.heex file and it would render the form.

<%= AppWeb.ItemController.new(@conn, nil) %>

and it would render the form embedded. But it doesn’t! It just renders the form and “forgets” about the rest of the file.

I’m quite at a loss at what I’m missing here.

I hope I was clear and thank you for your attention!

You could turn the part that you want to render in multiple places into a function component and use that in new and index.

2 Likes

Hi,

in this case, I think you can use either:

<%= new(Map.put(assigns, :action, Routes.item_path(@conn, :create)) %>

or

<.new {Map.put(assigns, :action, Routes.item_path(@conn, :create))}/>

Since new.html.heex template is now compiled as def new(assigns) (previously def render("new.html", assigns)) (see Phoenix.Template.render/4 for details)

To include a partial from a different view, you’d have needed the following:

<%= AppWeb.ItemHTML.new(Map.put(assigns, :action, Routes.item_path(@conn, :create)) %>
2 Likes

Thank you @t0t0, this was the solution! Which is weird, I wasn’t really getting it to work when I called AppWeb.ItemController.new instead of just new.

One detail though. Routes.XXX_path isn’t dynamically generated anymore when generating a new model with mix phx.gen. So Routes.item_path is undefined.
I ended up using the idiomatic way that is present in the Phoenix 1.7rc docs. This is how I did it.

<%= new(Map.put(assigns, :action, ~p"/items/new")) %>

Thank you once again! And thank you @cmo as well, I think that would have worked as well! :slight_smile:

2 Likes

To directly replace:
<%= render "new.html", Map.put(assigns, :action, Routes.item_path(@conn, :create)) %>
You could use:
<%= Phoenix.Template.render MyApp.Module, “new”, “html”, Map.put(assigns, :action, ~p"/items/new") %>

1 Like