Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Better base_template solution #125

Closed
rragan opened this Issue · 10 comments

3 participants

@rragan
Owner

The current base/child template solution is confusing and non-intuitive in how it works (see issue #101). I believe a much cleaner approach would be as follows:

1) Define the base template as is done currently using {+xxx} to define default content and a named replaceable block.
2) Use the base template as a partial just like now but with a compiler extension to allow blocks in a partial.
3) A small change in the partial method to handle the newly allowed blocks

Here is an example:
Define base_template as in the original example:
Start{~n}
{+title}
Base Title
{/title}
{~n}
{+main}
Base Content
{/main}
{~n}
End

Now in child_template

{>base_template}
{:title}
Child Title
{:main}
Main Content
{/base_template}

With the above changes, the result would be the same output as the current mechanism but with no need to rely on the inline partials mechanism.

The problem posed in issue #101 would be written as below and ought to do what the writer expected.

{>input_skeleton}
{:input}
{?disabled}
DISABLED INPUT
{:else}

{/disabled}
{/input_skeleton}

Of course, since partials accept params, the default and override content blocks could contain parameter names and have inline values substituted into the default or override content providing additional flexibility.

Note that this does not break the old way of doing base layouts. It will still work but likely would not be the advertised way of doing things.

A quick proof of concept I did that hacked the generated code to add blocks to the partial call showed that this does work as expected.

@vybs
Collaborator

A partial is an external file, why should it allow a non self closing tag. Not quite please with the syntax : {>base_template}
{:title}
Child Title
{:main}
Main Content
{/base_template}

The only reason the author create a < is to distinguish the external file and create inline partials.
I 'd keep the semanatics as he did before. except use

{<<input}DISABLED INPUT {/input} to not make this inline partials global ( evulate them based on conditionals. i. e in the context of which it is declared )

I sometimes wish @akdubya wakes up and gives us more context into his philosophy. !

@rragan
Owner

True, a partial is an external file and I'm not suggesting that be changed. In this experiment, it needs to allow a non-self closing tag so that one or more bodies can be expressed much as they can be in helpers and sections (the other non-self closing or just plain key tags).

The current way inline partials works just wigs me out. In effect they are the equivalent of constant declarations that are hoisted to the top of the template and the last one written wins. But they don't look anything like that. I think I just managed to convince myself that their scope is restricted to the defining template (as documented) and so their globality is less perturbing than I thought it was.

I just noticed that something like
<title name=pageName}
Child Title {name}
{/title}

is not considered an error but generates no code for what the user probably thought was a subsitutable title name.

My approach was to use named bodies to provide the definition for named blocks {+name} rather than use inline partials.

@vybs
Collaborator

yes : this was my understanding too: It is restricted to the defining template (as documented) and so their globality is less perturbing than I thought it was

( may be good thing to add it in documentation )

Without reading the documentation, it is not a good thing to assume
the <title ...will render/ print something

Yes, it may not be intuitive, But it is clearly documented that { +/} are a way to reference and { < } are like placeholders

it is even restricted by the # block scope as I commented in issue #101

I still need time to think thru the proposal you made, I agree that ruby developers may find the + non intuitive, but
then it is not super witchcraft either

@vybs
Collaborator

@rragan a good post on how handlebars does it , gives a nice history ...
http://thejohnfreeman.com/blog/2012/03/23/template-inheritance-for-handlebars.html
Template inheritance comes, I believe, from Django. It nicely addresses the above issues with template composition by essentially providing a mechanism for implementing the Dependency Inversion Principle.

I wish @akdubya comes alive someday and explains his influences!

@vybs
Collaborator

@rragan and @patrick-steele-idem

Looks like mustache community woke up ...

mustache/spec#38 (comment)

@rragan
Owner

I'd like to revisit templating in dust. As noted I'm not enthused with the current inline partial system.

  • Users get confused because the compiler statically compiles the block and if duplicated only the last one is used (Issue #101 and #197)
  • The syntax of inline partials does nothing to lead you to know that any layout operation is going on

Earlier in the thread I postulated adding blocks to partials. I'm switching to helpers for my latest exploration since they support blocks. The use of {+name} in the base template is unchanged from what exists now. The only change is the way the values for the blocks are specified. The general syntax is:

{@layout base="name of partial defining the layout"}
{:title}
body for "title"
{:main}
body for "main"
{/layout}

Let's look at some earlier reported issues for examples: First is #197. The underlying control-group base template remains as is. To use it you would write:

{@layout base="control-group"}
{:label}<label for="name">Name:</label>
{:inputs}<input type="text" name="name" id="name" />
{/layout}
{@layout base="control-group"}
{:label}<label for="name">Age:</label>
{:inputs}<input type="text" name="age" id="age" />
{/layout}

For Issue #101:

{@layout base="input_skeleton"}
{:input}
{?disabled}
   DISABLED INPUT
{:else}
      <input name="{name}" maxlength="{maxlength}" class="{class}"  value="{value}" type="text"/>
{/disabled}
{/layout}

Finally, let look at multiple level inheritance:

level2.dust
{@layout base="level1"}
{:title}
This is the title of level2
{/layout}

level1.dust
{@layout base="baseTemplate"}
{:mainContent}
This is level1 Content
{:title}
This is the title of level1 {title}
{/layout}

baseTemplate.dust
{+title/}

{+mainContent}
Default content
{/mainContent}

Then a call of
{>level2 /} would output This is the title of level2This is level1 Content

The rules are consistent with those of other templating language solutions supporting multiple levels of inheritance.

  • Any blocks defined by the highest level override any defined by lower levels
  • Any block not yet defined by a higher level can be defined by a lower level
  • Any block not defined by the time you reach the lowest level, uses the default content within the {+name}xxx{/name}

The helper to implement this is:

helpers.layout = function (chk, ctx, bodies, params) {
    var base = dust.helpers.tap(params.base, chk, ctx);
    var prevBlocks = ctx.blocks?ctx.blocks[ctx.blocks.length-1]:{};
    for (var key in prevBlocks) {
      bodies[key] = prevBlocks[key];
    }
    ctx.blocks = [bodies];      
    return chk.partial(base, ctx, null);
};
@nistormihai

I have multiple level inheritance and the same problem arise when I tried to hide html markup:

PS: stupid chrome and old ie-s show broken image
when an image is empty

I'm a little bit reluctant to implement the layout helper that @rragan gave but I will give it a try caz I really need something like this fast.
Anyhow hopefully this blocks issue will be just like django block in the future
And :+1: @vybs "I wish @akdubya comes alive someday and explains his influences!"

@nistormihai

Base template:

Start{~n}
{+title}
Base Title
{/title}
{~n}
{+main}
Base Content
{/main}
{~n}
End

Child template:
{>base_template/}
{^xhr}
{<main}Main child {/main}
{:else}
{<main}Main child true that{/main}
{/xhr}

Will always show:
Start
Base Title
Main child
End

No mater if the xhr is true or false.

@rragan
Owner

current impl resolves templates at compile time with last one statically present being used. Therefore, runtime checks like those on xhr have no effect. Something better is needed.

@rragan
Owner

I'm ok with a helper based solution I have.

@rragan rragan closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.