Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Phoenix.Template only compiles templates in the immediate directory #1228

Closed
stevegraham opened this issue Sep 21, 2015 · 13 comments
Closed

Comments

@stevegraham
Copy link
Contributor

Hi all,

I found that that Phoenix.Template.__before_compile__/1 does not recurse through subdirectories when compiling templates and only compiles the templates in the immediate directory. I think I've tracked this down to Phoenix.Template.find_all/1, i.e. the path search does not include a glob parameter. The docs for Path.wildcard/1 indicate ** will match 0 or more subdirectories. Perhaps this can be fixed by changing the pattern to Path.wildcard("#{root}/**/*.{#{extensions}}")?

My use case is conditionally rendering a template depending on the state of the resource, e.g. Enrolling a bank account in http://teller.io. There are many supported banks and enrollment is a multistep process so I want to do something like.

defmodule App.EnrollmentController do
  use App.Web, :controller

  # ... snip

  def show(conn, %{"id" => id}) do
   # ... load enrollment
    render_enrollment conn, enrollment
  end

  defp render_enrollment(conn, %{state: "enrolled"} = enrollment) do
    render(conn, "show.html", enrollment: enrollment)
  end

  defp render_enrollment(conn, enrollment) do
    render(conn, "#{enrollment.institution}/#{enrollment.state}.html", enrollment: enrollment)
  end
end

This currently raises a `Phoenix.Template.UndefinedError

@chrismccord
Copy link
Member

This is by design because it's much simpler. Would it be feasible to use another view per institution and render templates that way?

@stevegraham
Copy link
Contributor Author

It would be possible but not ideal because:

  • Unnecessary boiler-plate code, i.e. a plethora of empty view modules
  • a cluttered top-level view directory, (unless I did some fancy macro stuff to infer root, which would add to the clutter.

@chrismccord
Copy link
Member

How man institutions are we talking? Can you forgo nesting and use a prefix instead?

render(conn, "#{enrollment.institution}-#{enrollment.state}.html", enrollment: enrollment)

@stevegraham
Copy link
Contributor Author

I think that's what I'm going to do for the moment until either this is implemented in Phoenix or I dream up a different abstraction 😊. I have ~15 institutions in one territory (the UK), each one has 3-5 states. Ultimately there will be a large number of institutions, i.e. the amount of banks in the world, institution == bank for all intents and purposes.

Sorry to bother you with what might seem like a niche requirement, times like these I really miss Ruby's open modules!

@josevalim
Copy link
Member

Yeah, a large directory will be the way to go. We will keep an eye open. If a lot of folks need this, we may make it configurable.

Also, let's not forget to move those discussions to the core mailing list folks. :)

@aseigo
Copy link

aseigo commented Aug 31, 2016

FWIW, my team recently ran into this same issue. The use case is similar to the OP's: we are building an application which needs a way to hygienically offer multiple sets of templates for the same core functionality.

We need this for whitelabeling support, which is often combined with a multi-tenant environment in which there is one deployed instance of the application (perhaps spread over several VMs / physical machines, but operating as a single instance) that needs to reflect both the primary brand as well as, depending on access details (e.g. domain name, path, headers, ..), customer brands. This is a bit more than simple skinning ("change the colours, move things around the page") and many times requires material adjustment to forms and other elements.

Installing and updating the whitelabel components for a customer (or "tenant") needs to be easy and provide some level of "won't conflict with other installed things" safety. Living in the same tree in a deployment is fine, but they do need to added (or removed) post-installation and not be contained in the main product directly.

The "easy" approach, and one the developers are familiar with from elsewhere, is to put each of these whitelabel bundles in their own package and install them in a subdirectory associated with the installed instance. We're looking at other possible approaches (after all: "what you know is not always the only, let alone best, way"), but having all the templates necessarily co-mingling in one directory is a bit unfortunate.

tl;dr -> we've hit the same "gotcha", though we are not sure it is a blocker .. certainly a bit of a thinker, though.

@josevalim
Copy link
Member

This has been added to Phoenix though in a later pull request. Please check
the latest docs.

On Wednesday, August 31, 2016, aseigo [email protected] wrote:

FWIW, my team recently ran into this same issue. The use case is similar
to the OP's: we are building an application which needs a way to
hygienically offer multiple sets of templates for the same core
functionality.

We need this for whitelabeling support, which is often combined with a
multi-tenant environment in which there is one deployed instance of the
application (perhaps spread over several VMs / physical machines, but
operating as a single instance) that needs to reflect both the primary
brand as well as, depending on access details (e.g. domain name, path,
headers, ..), customer brands. This is a bit more than simple skinning
("change the colours, move things around the page") and many times requires
material adjustment to forms and other elements.

Installing and updating the whitelabel components for a customer (or
"tenant") needs to be easy and provide some level of "won't conflict with
other installed things" safety. Living in the same tree in a deployment is
fine, but they do need to added (or removed) post-installation and not be
contained in the main product directly.

The "easy" approach, and one the developers are familiar with from
elsewhere, is to put each of these whitelabel bundles in their own package
and install them in a subdirectory associated with the installed instance.
We're looking at other possible approaches (after all: "what you know is
not always the only, let alone best, way"), but having all the templates
necessarily co-mingling in one directory is a bit unfortunate.

tl;dr -> we've hit the same "gotcha", though we are not sure it is a
blocker .. certainly a bit of a thinker, though.


You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
#1228 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAAlbryutQIYz6BPzNBKaef5nrwyC2r6ks5qlUjBgaJpZM4GBGHy
.

José Valimwww.plataformatec.com.br
http://www.plataformatec.com.br/Founder and Director of R&D

@stevegraham
Copy link
Contributor Author

❤️ ❤️ ❤️ ❤️

@aseigo
Copy link

aseigo commented Sep 2, 2016

For others who stumble here, the key is the setup of Phoenix.View in e.g. web/web.ex

def view do
quote do
use Phoenix.View, root: "web/templates", pattern: "*/"

note the use of "pattern" there, which is the new thing José is referring to above. This was added in 1.2.0.

Thank-you for the responses and for those who made this improvement a reality :)

@pauloancheta
Copy link

pauloancheta commented Nov 21, 2016

After making the change, I get Could not render "app.html" for App.LayoutView. Any suggestions @aseigo ?

@viniciussbs
Copy link

@pauloancheta Try use Phoenix.View, root: "web/templates", pattern: "*". It worked for me.

@lessless
Copy link

@viniciussbs did that work in compiled application as well? In my case adding pattern: "**/*" fixes problem in MIX_ENV=prod mix phoenix.server but doesn't work when app is compiled

@viniciussbs
Copy link

@lessless I don't know. The app that uses this pattern is not compiled. :-|

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants