How to serve different html for different controller actions/endpoints?

Hi all…

I am using bootstrap in my layout and having issues figuring out how I should structure the template files to keep them as DRY as possible. The main issue for me revolves around the bootstrap navbar and how I can include the navbar outline in the /layout/app.html.eex file and then control the navbar content individual controllers.

For example at the following endpoints:

/scenarios/new
/scenarios/edit

I need the menu option on the “new” page to be “save”, but on the “edit” page, I need it to read “update”. This is a contrived example (there are more differences to my menus than this), but hopefully gives an idea of what I am trying to achieve.

Currently, I have the entire navbars defined in the new.html.eex and edit.html.eex files, which works, but doesn’t feel like the best way to go about it.

Thanks for your time…

Found my answer here:

https://github.com/phoenixframework/phoenix/issues/811

EDIT - It wasn’t quite what I was looking for as the view fragements link I posted only really helps on a per view basis and doesn’t allow me to define what the additional HTML will be on a per REST action…

What does your code look like with that Nate?

I wonder if there is anything like controller.controller_name and controller.controller_action (that we have in Rails) for Phoenix?

Phoenix.Controller.action_name(conn) and Phoenix.Controller.controller_module(conn) besides a few more functions in that module.

2 Likes

Quick update on this, turns out I was wrong about the view fragments. I just took Chris’s example a little too literally.

So the following is what I ended up going with

/templates/layout/app.html.eex

<header>
    ....
     <%= render_existing @view_module, "navigation.#{@view_template}", assigns %>
    ....
</header>

Which then allow me to specify a particular navigation based on the controller action, so I defined the following files

/templates/scenario/navigation.show.html.eex
/templates/scenario/navigation.edit.html.eex

And of course if a particular action (e.g new - /templates/scenario/navigation.new.html.eex) isn’t defined then because render_existing returns nil, it isn’t an issue.

BTW - In case I hadn’t mentioned it already, scenario is a model I generated…

Seems to work ok, can anyone see an issue with this?

Cheers…

1 Like

This is what I do. I haven’t investigated any deeper but I am not 100% happy with it. I have a sidebar which is the same for a subset of the views. However, I end up duplicating the sidebar into every view directory where it applies which feels wrong.

I guess this applies to any “global” layout which needs action specific customization such as title bars and the like.

Would be interested in alternative ways to solve this.

For reference my solution was based on:

Right, I see what you are saying.

So far my solution will be fine for my two actions that are different, but for every other page that has the “common” layout, I will probably face the same issue.

BTW, with your first link, I saw the comments were raving about option C… Is that something you tried? It seems likes a logical solution (option c that is) - although I am hoping someone comes along with a “pro tip” telling me how that type of thing has already been implemented in Phoenix :slight_smile:

This gets me out of trouble for now as I am prototyping my app, but would be interested if there are other options.

1 Like

Nate - I would just create a helper using what @LostKobrakai posted above :023:

So in my html template I might have something like:

    <% if edit_or_update(@conn) do %>
    ///Code///
    <% end %>

The helper would be something like:

  def edit_or_update(conn) do
    Phoenix.Controller.action_name(conn) == "edit" || Phoenix.Controller.action_name(conn) == "update"
  end

(Please note untested code! I haven’t actually created a Phoenix app yet!)

Martin - In the case of more complex blocks, I would create partials for each in conjunction with the above. That’s what I’d usually do in Rails app anyway (haven’t done any Phoenix apps yet).

Hopefully someone with more Phoenix experience can confirm whether this is a good approach…

No, if I remember correctly I didn’t quite like that approach because it groups a whole lot of different parts of the application into one Module.

Ok, true, but it does seem like the more flexible (only) solution that stops you having to copy the same template across multiple view directories. At least from what I understand so far…

AstonJ… I am not sure I like the idea of putting conditionals in my template like that. I think option C in the link cmkarlsson (first link) mentioned would be a better spot for that?

Either way, it feels like I will either have to go some form of the conditional route or do a whole bunch of copying files…

I wonder if you could softlink back to a template file rather than copying to different view directories?

EDIT

Sorry, that didn’t quite make sense. While you would still have to do the copy, if you softlinked back to a single file, at least you could edit the template in a single location

This has worked for me, using that you could create a shared template that does the conditional rendering (in a single place) of other templates as you see fit. (I think if you want to display something based on a condition somewhere along the way you’ll need to decide it conditionally?)

Like
= render_shared "decidorz.html", assigns

And in decidorz.html decide what you actually want to render

<%= if “it meows” %>
<%= render_shared “milk.html”, assigns %>
<%= else %>
<%= render_shared “water.html”, assigns %>
<% end %>

(unless I’m not understanding what you need)

2 Likes

Thanks.

Yes, true… however the render_existing approach using the view module and template does all that for you without having to write conditionals (which is why I still like that approach). I have no particular problem with conditionals but I “feel” like they might be a maintenance issue down the track… maybe not.

Whereas the render_existing method purely relies on the convention of you specifying the correct template file in the correct directory.

EDIT

BTW, I still don’t know what the best approach is here and maybe it comes down to a per project decision as opposed to a general decision that will work for all projects…