12

In the Phoenix Framework is there a common technique for setting a page title based on a route/path. Or is this just a matter of calling assign(:page_title, "fred") at the right point inside my routed function?

Update

I ended up implementing a variation of @michalmuskala's solution. I pass up the action name instead of @view_template:

<title><%= @view_module.title(action_name(@conn), assigns) %></title>

Then in the view module the code looks like this:

def title(:show, assigns), do: assigns.user.name <> " (@" <> assigns.user.user_name <> ")"
def title(:edit, _assigns), do: "Edit Profile"
def title(_action, _assigns), do: "User related page"

The last statement in the above code is an optional "catch all" for the module (and is something I'll probably only do while transitioning)

Mitkins
  • 4,031
  • 3
  • 40
  • 77
  • 2
    I usually just pass `title: "The Title"` to the `render` call from each controller action. You could use plugs if the page title is based on some logic applied to the controller name and/or action name. – Dogbert Feb 21 '17 at 08:41

2 Answers2

32

A nice approach to handling titles is to realise that view is a module like every other one. This means you can define additional functions on it. On the other hand, in the layout you have access to the current view module - this means we can call the function we defined earlier.

Let's see how that would work in practice:

# The layout template
<title><%= @view_module.title(@view_template, assigns) %></title>

# In some view module
def title("show.html", _assigns) do
  "My awesome page!"
end

Thanks to passing both template name and assigns to the title function it works exactly like render/2 - we can pattern match on the template name and have access to all the assigns. We're calling the function unconditionally on all the views, so it has to be defined on all the views - we could add some additional check with function_exported?/3 and some default fallback title, but I think being explicit and defining it in every view isn't that much work and makes for a simpler code.

michalmuskala
  • 11,028
  • 2
  • 36
  • 47
  • 2
    Consider to add this to documentation, because now Rails is many steps ahead in terms of using templates includes. Very nice solution, worth to note somewhere else. – PatNowak Feb 21 '17 at 19:20
  • Won’t the dedicated `Plug` be more natural here? Am I missing something? – Aleksei Matiushkin Feb 22 '17 at 16:31
  • 4
    Doing this in a plug would be confusing concerns. The plug would live in a controller or a router - so now setting a title, which is a concern of UI exclusively, is leaking all over the codebase. Keeping it in view, allows to keep different things separate and much easier to maintain in the long run. – michalmuskala Feb 22 '17 at 20:32
0

I got an error trying this "assign @view_module not available in template".

[UPDATE]

Turns out the new solutions is view_module(@conn) and view_template(@conn). E.g.

<%= view_module(@conn).title(view_template(@conn), assigns) %>
trans
  • 1,411
  • 1
  • 13
  • 13