Hi,
I am wondering how difficult it would be to add a default and generic ApplicationView
. Currently, most of my views are empty. For example:
defmodule Myapp.Web.UsersView do
use Myapp.Web, :view
end
defmodule Myapp.Web.CommentsView do
use Myapp.Web, :view
end
# etc
I tried introducing plug :set_default_application_view
to the :browser
pipeline. All it does is simply put_view(conn, ApplicationView)
. However, I quickly understood that the path to the template is built based on the name of the “View” itself.
What I’m trying to achieve is removing all those blank views and replacing them with a default `ApplicationView’ that renders the templates based on the name of the controller.
i.e. UsersController
should use ApplicationView
by default and render templates from the templates/users
directory.
How difficult would it be to achieve that?
Also, does it make sense to make it a default … in Phx 1.3 maybe
Doesn’t render/4 fit your use?
The views aren’t really blank since the accompanying templates become methods in the view.
Surprised you don’t have any additional helper logic for your views right now? That’s generally where that stuff goes.
I don’t thin so. As far as I tested (and checked the source code), if I using something like that:
render conn, Myapp.Web.ApplicationView, "show.html", user: user
The relevant template path will be “templates/application”, and I’d like it to be under the name of the controller. e.g. “templates/users”.
Also, I’d like to use a simpler render
- like this one render conn, user: user
Hm, maybe I have to read a bit further about how templates work … thanks.
You can get what you want with the following, but I wouldn’t want to compute the controller subdirectories for every request. As other have said, a better option would be to simply plug put_view
and call render/3
with an explicit template name, i.e: render(conn, "user/index.html", ...)
. If you really want what you describe you can do the following, but I would opt for the former:
# you in your pipeline
plug :put_view, {MyApp.Web.AppView, "app.html"}
# in your :controller block in web.ex
import Phoenix.Controller, except: [render: 2]
import MyApp.Web.Controller
# in your :view block in web.ex
use Phoenix.View, root: "lib/my_app/web/templates",
namespace: MyApp.Web,
pattern: "*/**"
defmodule MyApp.Web.Controller do
def render(conn, assigns) do
view = Phoenix.Controller.view_module(conn)
template =
conn
|> Phoenix.Controller.controller_module()
|> Module.split()
|> List.last()
|> Phoenix.Naming.unsuffix("Controller")
|> Phoenix.Naming.underscore()
|> Path.join(action_name(conn))
Phoenix.Controller.render(conn, view, template, assigns)
end
end
# in your controllers
render conn, foo: bar
# your app view
defmodule MyApp.Web.AppView do
use MyApp.Web, :view
end
# your web/templates dir
#
# templates/
# app/
# user/
# show.html.eex
# product/
# show.html.eex
# profile/
# show.html.eex
# edit.html.eex
# whatev/
# ...
That’s an awful lot of work to avoid potential empty view modules that are ready for presentation layer functions as soon as you need them. tldr; use multiple view modules and carry on happily with life
2 Likes
Thanks a lot @chrismccord!
but I wouldn’t want to compute the controller subdirectories for every request
Isn’t that already done for computing the subdirectory based on the name of the view module?
UsersView
=> “users/” subdirectory. UsersController
=> “users/” subdirectory. i.e. we are not adding additional calculations but rather switching them from calculating based on the View’s name to calculating based on the Controller’s name… or am I missing something?