Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Proposel: dynamic partials #49

Open
drekka opened this Issue · 15 comments

5 participants

@drekka

Hi all, I'm new to moustache so please correct if there is a way to do this. I've been using Mu2 and Node.js as a bit of context.

I could like to be able to create a skeleton moustache template and then tell it which partials to include. The reason for this is that using this top down approach to assembling a document means that each template only needs to be aware of the content it contains, not that which is around it. An example is this - with some help from the Mu2 developer I current need templates like this:

view

{
    heading: "MY content heading",
    content: "<p>Some text</p>"
}

Main render: content.mustache

{{> header}} 
{{heading}}
{{& content}}   
{{> footer}} 

Partial: header.moustache

<html>
    <headers><!-- other partials here --></headers>
    <body>
        <!-- menu bar partials, etc -->

Partial: footer.moustache

    </body>
</html>

This will work for the moment, but it's rather ugly because the content has to know what goes around it. It also has problems if I need (for example) to select different partials in the headers and the content. What I would much prefer to be able to do is this:

view

{
    contentPartial: "content",
    heading: "MY content heading",
    content: "<p>Some text</p>"
}

Main render: page.moustache

<html>
    <headers><!-- other partials here --></headers>
    <body>
        <!-- menu bar partials, etc -->
        {{> contentPartial}}
    </body>
</html>

Partial: content.mustache

{{heading}}
{{& content}}   

So when the rendering encounters the {{> contentPartial}} code, it detects that contentPartial is a value in the view, insert's it's value and then continues to include, thus including the content.moustache partial.

Please let me know if there is a way around this issue - lamda's perhaps?

@pvande

Lambdas can get you around the issue fairly well:

// Data
{
  heading: "MY content heading",
  content: "<p>Some text</p>",
  layout: function(raw) { return "{{>header}}" + raw + "{{>footer}}" }
}
{{#layout}}
  {{heading}}
  {{> content}}
{{/layout}}

On the other hand, it sounds like what you actually want is a proper "layout" facility. Something like:

<html>
    <head></head>
    <body>
        {{> $partialName}}
    </body>
</html>

Where partialName is a symbolic reference to a partial, not a partial itself. The Mustache spec doesn't presently allow for this kind of behavior; in the past, I've worked around this with hacks like {{= | | =}}{{> |partialName|}} (and then rendering the template twice).

We need a better answer for v2.0.0.

@drekka

Ahh, I see. using a lamda like this means that the layout is now embedded in the code. Workable but as you say, I would much prefer a layout facility as per your example. I would second adding that.

@pvande

Here's another proposed solution to this problem:

<!-- layout.mustache -->
<html>
 <head></head>
 <body>
   {{{yield}}}
 </body>
</html>
<!-- content.mustache -->
{{#> layout}} <!-- Renders the named partial into this block, mapping the existing contents to a "special" key -->
   <div id="content">
     <p>Blah blah blah ...</p>
   </div>
{{/ layout}

From mustache/mustache#138.

@pvande

Another potential solution involves the creation of "dynamic content" inside templates.

<!-- layout.mustache -->
<html>
  <head>
    <title>{{> title}}</title>
  </head>
  <body>
    {{> content}}
  </body>
</html>
<!-- content.mustache -->
{{> layout}}

{{< title}}Page Title{{/ title}}

{{< content}}
  <div id="content">
    <p>Blah blah blah ...</p>
  </div>
{{/ content}}

This may have some order-related issues if implemented improperly, but stands to be a fairly simple approach overall.

From mustache/mustache#138 (comment).

@trans

@pvande This is template inheritance you mention, yes? (a la #38) I think the functionality it provides is a good idea, but I find the proposed notation intolerable to read, as well as lacking in a certain functional respect. Specifically the top {{> layout }} is confusing. The notation on its face indicates a partial should be inserted right there, regardless of what follows. In addition it seems like this disallow the use of more than one "layout" in the same "content" document.

To remedy, I would propose instead an approach that I think is both more readable and reusable:

<!-- layout.mustache -->
<html>
  <head>
    <title>{{{title}}}</title>
  </head>
  <body>
    {{{content}}}
  </body>
</html>
<!-- content.mustache -->
{{#> layout}}
  {{=title}}Page Title{{/title}}

  {{=content}}
    <div id="content">
      <p>Blah blah blah ...</p>
    </div>
  {{/content}}
</ layout>

Notice there is nothing special about layout.mustache, it could be reused just as easily without template inheritance.

The content still uses a "block partial" within which definitions can be made via named blocks (i.e. {{= name}}). This allows more than one layout template to be used.

This could also remain compatible with original yield variation you mention above when no named blocks are provided.

@pvande

In this case, what I'm describing isn't actually template inheritance -- that {{> layout}} you mentioned is intended to be exactly the partial include it appears to be. The difference is that the {{< title}} block (analogous to your {{= title}} block) declares the content for the named partial, which the layout partial makes use of. Part of the confusion may come from the fact that I poorly chose the same name for a "dynamic partial" and the actual template file.

If, as you point out, it looks like you could include the {{> layout}} tag in multiple places, that's because you can.

The choice of sigil in the description is for illustrative purposes only. It could just as easily have been +, *, @, or $.

As for the functional respect, I actually think this solution could leverage one of the most valuable aspects of Mustache. With very few exceptions ({{= start end =}} being the primary offender), very little about Mustache is procedural. If we work from the perspective that it is (/ should be) a declarative templating language, features like these fall out fairly naturally.

For all this, we're still just collecting suggestions at this point -- we don't have to make a decision right now, and I'd like us to get a few more thoughts written down before we do. Your proposed solution has some intersting merits: "overrides" seem to be very specifically scoped, and you're actually providing new values for interpolation. Great food for thought, thank you.

@MattCheely

If you take the approach of

{{> layout}}

{{< title}}Page Title{{/title}}

How do you deal with a situation where you want to re-use the partial more than once? like this?

{{> person}}

{{< name}}Bob{{/name}}

{{> user}}

{{< name}}Jane{{/name}}

Or is that not an intended use case?

It seems very odd. There's nothing that structurally ties the {{< name}} blocks to the partials other than ordering. What happens if I put regular content between the partial and the next {{< foo}} block?, or declare a `{{< foo}} without a relevant partial beforehand? That syntax seems very ambiguous to me - there are a lot of odd things you could do to mangle it and it's not at all intuitive as to what the expected result from them would be.

@pvande

@mcheely The envisioned behavior of the {{< title}}...{{/ title}} construct would be the declaration of a new partial with that name. Put another way, the thing that ties things together is the name, not the order. Whether written before or after any use of the corresponding partial include ({{> title}}) should be irrelevant.

Regular content in the template behaves as regular content in any other template. Duplicate declarations within the same compilation unit are an error; in resolving multiple declarations, the nearest (inherited) one wins.

These examples would all be equivalent:

{{! Example 1 -- Partial declared first }}
{{< foo}}***{{/foo}}
[{{> foo}}]

{{! Example 2 -- Partial declared after use }}
[{{> foo}}]
{{< foo}}***{{/foo}}

{{! Example 3 -- Partial includes sibling partial declaration }}
{{> layout}}
{{< layout}}
[{{>foo}}]
{{< foo}}***{{/foo}}
{{/layout}}

{{! Example 4 -- Partial includes parent partial declaration }}
{{> layout}}
{{< layout}}
[{{>foo}}]
{{/layout}}
{{< foo}}***{{/foo}}

{{! Example 5 -- Sibling partial declaration overrides parent partial declaration }}
{{> layout}}
{{< layout}}
[{{>foo}}]
{{< foo}}***{{/foo}}
{{/layout}}
{{< foo}}xxx{{/foo}}

Remember that in this model, the {{< tag}} construct is identical to a partial template file, except that it masks existing partial files, and is inherited by partial inclusion.

Those examples help?

@trans

There's two things your examples really makes clear to me.

1) The use of both < and > really is dizzying. I find myself saying "wait...which is which again?".
2) "Declarative" non-order dependent design can turn templates into spaghetti code real quick.

At least that's how it seems to me... It really just seems more complex than it needs to be.

@pvande

@trans I agree completely about < vs >. I'm not in favor of any proposal that implies a meaning for < that is not simply "oops, you probably meant >". I've used it here only for consistency's sake.

As for the non-order dependent design adding needless complexity, I really can't say. I feel like we'll probably need to do a proposal shootout at some point (with a non-trivial example) to gauge viability. The spec currently only defines two cases in which order of evaluation matters (delimiter changes and stateful functions / methods), and I'm inclined to move away from order-dependency as much as possible.

I feel at this point as if I'm being drawn in to defend a decision that hasn't been made. At this point, the proposal's been made, and I've clarified a few points around what the initial proposal was intended to describe. Barring massive communication failure on my part, I intend to avoid further comment on this proposal.

@trans

No problem. I think it's been a good discussion. I think there's a tendency to underestimate the value of detailed dialog like this in figuring out best approaches. I don't think it's so much a matter of defense, as it is just putting pros and cons of variant viewpoints on the table (in print) so they can be easily deliberated.

@MattCheely

@pvande That does clarify things, thank you. Since you asked, I'll refrain from another round of comments. I completely understand that what's here is just a proposal. This does lead me to a few other questions & comments though:

  • Is there any kind of structured progress ongoing for solving this sort of issue and/or developing version 2.0 of the mustache spec?
  • If so, what's the correct way for those of us using mustache to participate / when should we start doing so?
  • If you ever need real non-trivial examples for the sort of behavior discussed in this post, we are using mustache.java and hogan.js at Lulu & have several use cases currently implemented via the inheritance proposal from issue #38
@pvande

@mcheely The formal process at this point is that this issue tracker is being used to aggregate and discuss spec-level problems with Mustache. When there's a clear path forward, one of the spec curators will write (or merge) the changes for review. I'm going to be opening a "v2.0.0 metaissue" shortly, in the hope that we can more concretely designate the things we want to see changed in the next major version of the spec.

The best way to participate in this process is to keep engaging with this issue tracker, and with the spec itself.

If you have non-trivial real-world examples of problems or solutions, please share! At the very least, it gives us something concrete to discuss. :)

@groue

@pvande, GRMustache has shipped with an implementation of dynamic partials seen as an extension of 0-arity lambdas (variable lambdas, not section lambdas).

Discussion was in issue #54.

Documentation is at https://github.com/groue/GRMustache/blob/master/Guides/helpers.md. Especially, read the three "Variable helper example" sections.

The last example, "Variable helper example: have objects able to render themselves", is based on the following rule that the spec would benefit from embedding:

  • variable lambdas are interpolated with themselves as the top object in the context stack.

The same release also introduces this rule:

  • {{items}} should be rendered identically to {{#items}}{{.}}{{/items}} if the value is a collection.

Basically, it gives Mustache the ability to render {{items}} the same way as Rails renders <%= render @items %>.

I'm looking forward to your feedback, @pvande.

@groue

Dynamic partials need special care when templates are stored in a hierarchy of directories.

GRMustache has just shipped with support for both relative and absolute paths to partials.

Regular partial tags contain relative paths: {{> header }}, {{> partials/header }}.

Absolute paths start with a slash: {{> /path/to/partial }}.

Release notes, and link to documentation: https://github.com/groue/GRMustache/blob/master/RELEASE_NOTES.md#v540

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.