dragonchild has asked for the wisdom of the Perl Monks concerning the following question:

This is with the HTML::Template v2.6
Stuff here <TMPL_INCLUDE NAME="<TMPL_VAR NAME="DYNAMIC_INCLUDE">"> Stuff here

That doesn't work. I read the code, so I know why it doesn't work. I wanted to do it because I have a reports page that displays the report in the bottom half, should there be a report chosen. And, I found a work-around, but I was hoping to see if anyone has a better option.

  1. Use a dummy variable, like <DYNAMIC_INCLUDE> in the template.
  2. When getting ready to print, read in both the main template and the template you want to include.
  3. $main =~ s/<DYNAMIC_INCLUDE>/$sub_template/g;
  4. Pass $main to HTML::Template as a scalarref.

Problem: When you pass as a scalarref, you also need to pass the directory where your other templates are, otherwise your static <TMPL_INCLUDE>s don't work.

As a sidenote, how would this be handled in TT?

------
We are the carpenters and bricklayers of the Information Age.

Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Replies are listed 'Best First'.
Re: HTML::Template .... misfeature?
by LTjake (Prior) on Dec 11, 2003 at 14:46 UTC

    Yeah, sometimes you want to do dynamic includes with HTML::Template. It's come up on the list before.

    The first thread returned from the above link suggests using two H::T objects (source):

    my $t = HTML::Template->new( "foo.html" ); my $i = HTML::Template->new( "inner.html" ); $t->param( INNER => $i->output( ) );

    That way, having a TMPL_VAR called INNER in your main template will allow you to dynamically stuff the contents of another template inside.

    It's a bit backwards, but I've used this method before...

    Update: Instead, you may wish to write a filter (source)

    --
    "To err is human, but to really foul things up you need a computer." --Paul Ehrlich

Re: HTML::Template .... misfeature?
by !1 (Hermit) on Dec 11, 2003 at 15:45 UTC

    Hi dragonchild,

    Unfortunately there are much deeper problems as far as dynamic includes are concerned. You could go the way of including a filter, however, this won't work the way you expect it to if you cache the parsed template (the filter doesn't get processed on cached or file_cached templates). The most common solution to this problem is to go ahead and process one of the inner templates and include it as a parameter to the outer. If you've been following the html-template mailing list recently, there's been a good amount of talk concerning changes Sam Tregar hopes to implement in version 3.0. It seems that the changes would allow templates to be included dynamically if not directly through an included method then at least by changing the template's parse structure itself.

    Of course, I know of a couple of projects currently being worked upon to implement some new features on top of HTML::Template, however, I'm unable to recall any of them at this moment.

Re: HTML::Template .... misfeature?
by cees (Curate) on Dec 11, 2003 at 15:52 UTC

    The reason this doesn't work in HTML::Template is because TMPL_INCLUDEs are handled in the first parsing phase, and TMPL_VAR's and every other TMPL_ tag are handled in the second parsing phase.

    The first phase parses the template and checks to make sure all tags are balanced and consistent, and also reads in and merges any TMPL_INCLUDEs leaving one big template. The results are then optionally cached in case they are needed for a second run.

    By the time the second phase runs, which does the actual variable substitutions, there is no record of what was an include, everything looks like one big template.

    There is an alternate (very simple) way that you could handle it, using simple TMPL_IF conditionals:

    Stuff here <TMPL_IF NAME="NEED_INCLUDE_1"> <TMPL_INCLUDE NAME="INCLUDE_1"> </TMPL_IF> <TMPL_IF NAME="NEED_INCLUDE_2"> <TMPL_INCLUDE NAME="INCLUDE_2"> </TMPL_IF> - or with HTML::Template::Expr - <TMPL_IF EXPR="DYNAMIC_INCLUDE eq 'include_file_1.tmpl'"> <TMPL_INCLUDE NAME="include_file_1.tmpl"> </TMPL_IF> Stuff here

    But keep in mind that every one of the includes will be read in and parsed in the first phase, so it can be expensive if you have a lot of dynamic includes. Using the caching system will alleviate that somewhat. It is also made somewhat more awkward since HTML::Template doesn't have an ELSIF tag.

    Your best bet is probably to go with the 2 Template object method explained by another poster...

    - Cees

Re: HTML::Template .... misfeature?
by jdtoronto (Prior) on Dec 11, 2003 at 18:00 UTC
    You basically got this answer form someone else already I think, but here is how I do it. I use a single Template for the bulk of my page - with the exception of a form. This particular project already has 150 or so forms.
    my $page = $self->load_tmpl('page.tmpl'); my $form = $self->load_tmpl('ap_qsign.tmpl', die_on_bad_params => +0); $form->param($errs) if ref $errs; $page->param('form_text' => $form->output()); return $page->output();
    This is from an app based on CGI::Application. The two templates get loaded, in the form template the VAR substitution is done, then the output of the form is substituted in a normal TMPL_VAR, in this case form_text and then the output of the combined objects is returned and output.

    Going back to your original question, the Problem of pointing to template directories is interesting. I had forgotten about it entirely because CGI::Application gives us a tmpl_path method that sets the template location.

    Good luck!
    jdtoronto

Re: HTML::Template .... misfeature?
by perrin (Chancellor) on Dec 11, 2003 at 18:43 UTC
    To answer your question about TT, since it has a fancier (and slower) parsing engine it is able to do it like this:

    [% INCLUDE $template_name %]