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

Feature Request: Inline partials #63

Closed
spudly opened this issue Jan 15, 2013 · 62 comments
Closed

Feature Request: Inline partials #63

spudly opened this issue Jan 15, 2013 · 62 comments

Comments

@spudly
Copy link

spudly commented Jan 15, 2013

Partials are great, but it'd sure be nice to be able to define partials right inside the template that uses them. In most cases, the partials I create are only used by one parent template so it's a lot of extra work to create the partial the way it works right now. Also, a lot of my partials are one-liners. As a result, I often times end up just copy/pasting instead. It sure would be nice if we could define partials inline. See issue #59. This would allow us to write mini-templates that can be reused within the same template. Here's some possible syntax:

Definition:

{{*mypartial}}
    <li title='{{title}}'>{{text}}</li>
{{/}}

Usage:

<ul>
  {{#items}}
    {{**mypartial}}
  {{/items}}
</ul>
@spudly
Copy link
Author

spudly commented Jan 15, 2013

The example I gave was not a use-case... more just to define a possible syntax (single asterisk for definition, double asterisk for usage). @devinrhode2 came up with the syntax in #59.

Inline partials just make it easier to define reusable code snippets. Some partials do not need to be shared between templates, so there's no reason to go through all the work to define them in their own mustache template file. That's the use case this helps with.

@devinrhode2
Copy link

I'm curious where basic iterations would not take care of this. Can you think of a good scenario where inline partials are better than a basic iteration?

@devinrhode2
Copy link

The syntax for outputting a partial is already {{> partial}} so essentially the question is: what's the syntax for creating a partial inline? Is {{* someNewPartial}} the best? Perhaps a block helper (if mustache gets those) could be used: {{#partial aPartial}} what do you think @groue?

@spudly
Copy link
Author

spudly commented Jan 15, 2013

I think you're right about the syntax. We should continue to use {{>partial}} for using the defined partials. How about {{>>partial}} for definition?

One good scenario is where you iterate over something more than once, or iterate over two lists with similar items. In that case, you don't want to just copy/paste. You need a partial that you can reuse. Here's a snippet:

{{>>person}}
  <a href='/user/{{username}}'>{{name}}</a>
{{/person}}

<h1>Democrats</h1>
<ul>
  {{#democrats}}
    <li>{{>person}}</li>
  {{/democrats}}
</ul>

<h1>Republicans</h1>
<ul>
  {{#republicans}}
    <li>{{>person}}</li>
  {{/republicans}}
</ul>

@devinrhode2
Copy link

{{>> partial}} is better for sure

Solid use case, thanks

@groue
Copy link

groue commented Jan 16, 2013

Hi all.

I think the use case still lacks proper definition.

If the motivation is to avoid partials and to be DRY, then Mustache already provides a solution called lambdas:

<h1>Democrats</h1>
<ul>
  {{#democrats}}
    <li>{{link}}</li>
  {{/democrats}}
</ul>

<h1>Republicans</h1>
<ul>
  {{#republicans}}
    <li>{{link}}</li>
  {{/republicans}}
</ul>

link being defined in the caller code as returning the rendering of <a href='/user/{{username}}'>{{name}}</a> (the API is implementation-dependant, thus no sample code).

If you have another motivation, @spudly, please refine it.

@spudly
Copy link
Author

spudly commented Jan 16, 2013

I reuse my mustache files with two different implementations (PHP and JavaScript). For this reason, I don't like using lambdas and have to stick strictly to the spec. Also, using lambdas for something like this would mean that I would have to write the code in one language and then port it to the other. Then if I ever discover a bug in one I have to be sure to fix both, not just one. No thanks.

Also, I precompile my templates using Hogan.js, which means I can't pass the contents of a lambda block to the lambda function (see twitter/hogan.js#75).

@groue
Copy link

groue commented Jan 16, 2013

Thanks @spudly for providing context.

If I understand your points, I regret your "No thanks".

Such a simple lambda would not be difficult to maintain in two languages. Seriously. It is not.

Mustache suffers from the start from a lack of cross-language fostering. I, however, see cross-language concepts and APIs the key to help Mustache become a strong template engine that does not collapse as soon as the application leaves the trivial zone.

Without an attempt at fostering solid and versatile APIs, Mustache will never go anywhere, and this repo will get more and more filled with issues that, seen from the outside, are all tiny additions that, if ever applied (and implemented by implementors), would turn Mustache into an ugly feature-bloated "tool" unable of having serious job done. I consider this as a sad waste.

Not that I think that "inline partials" are an ugly feature per se. What I say is that quick "No thanks" dismissals do not go in the right direction.

@spudly
Copy link
Author

spudly commented Jan 16, 2013

Please forgive my rudeness. I simply meant to say that I'd rather not maintain multiple copies of the same code. I do see the usefulness of lambdas; I just don't see them as a good solution to this particular problem.

You're right that creating a link is easy to implement and maintain using lambdas, however that was just a simplified example. I expect many inline partials would be muich more complex. Drawing a table row for example would be a pain to write in a typical programming language, but simple enough using a partial. In that situation I would probably have to write a lambda that then processes another template to create the row html and then returns the html... but then you've just reinvented partials.

Also, I believe there are scoping problems. Lambdas operate on a single parameter, that being the text within the lambda block. So {{mylambda}}foo {{somevar}}{{/mylambda}} becomes mylambda("foo {{somevar}}") (unrendered). There's no way to inherit the current variable scope to get the person's name and username.

@groue
Copy link

groue commented Jan 17, 2013

I would probably have to write a lambda that then processes another template to create the row html and then returns the html... but then you've just reinvented partials.

Also, I believe there are scoping problems

I think you are not lucky and that you use sub-par libraries that prevent you to do your job. I don't care if they are the most-used/forked/watched JS or PHP libs. I care that they don't help you. I care that in order to have your job done, you would need to tweak them so much they would collapse, wasted under your iron will. And thus you give up, and complain about Mustache in this very repo.

What you describe are plain and simple bugs in the implementations. Not in Mustache.

One needs to be able to write a lambda that processes another template, and not to be feared by that. The API should make this simple, not scary, and linked to partials instead of opposed to them.

One needs to write lambdas that inherit the current variable scope. If your Mustache implementation does not provide it, it's a toy, not a production tool.

@groue
Copy link

groue commented Jan 17, 2013

BTW, back to your topic. A user of GRMustache stores his templates in a custom home-made format, so that he can split them into named partials. He built is own inline-partial feature. Because he needed it. See for example https://github.com/tomaz/appledoc/blob/master/Templates/html/document-template.html

@spudly
Copy link
Author

spudly commented Jan 17, 2013

you use sub-par libraries that prevent you to do your job

It's not the libraries themselves. I don't expect the libraries to implement anything that is not in the spec. I want the feature added to the spec, so that both will implement it. That way I don't have to write two different template files to account for differences in the implementations. That's the whole point of having a spec, right?

And thus you give up, and complain about Mustache in this very repo.

Not trying to complain. Sorry if it came out that way. I'm simply requesting a feature be added to the spec and explaining why I think it would help.

What you describe are plain and simple bugs in the implementations. Not in Mustache.

A lack of a feature is not a bug. I did describe one bug that gives me grief with lambdas in Hogan.js, but that's the only one and I don't think it's even relevant to this issue.

One needs to be able to write a lambda that processes another template, and not to be feared by that.

I'm not scared to do it. I just don't see the point because I could just use a partial in the first place. Using lambdas to process another template seems like reinventing the wheel to me.

One needs to write lambdas that inherit the current variable scope. If your Mustache implementation does not provide it, it's a toy, not a production tool.

If I'm correct, the spec doesn't say that lambdas inherit the variable scope. Since I need this to work on multiple implementations, I only want to use features that are in the spec. This is why I put the feature request on the Mustache spec and not just on an implementation's repo.

Lambdas as designed in the spec do not and can not solve my problem. Sure, there may be implementations that can do so, but that's not in the spec so anyone who reuses templates across different implementations can't use them. That's the reason why I use mustache and not handlebars or any other mustache extension. Here is an idea for a feature that could solve a problem. It's DRY and logic-free. Many other template libraries have it. I think it would be a good idea to add this to the spec. If whoever approves/writes the changes to the mustache spec doesn't agree, that's fine.

@groue
Copy link

groue commented Jan 17, 2013

I just don't see the point because I could just use a partial in the first place. Using lambdas to process another template seems like reinventing the wheel to me.

That's precisely the kind of reasoning that happens because the implementations you are using are weak and force you into an uninspiring mental landscape. In these implementations, the versatility you are looking for is only accessible through partials. So you want more partials. And since those libraries provide inconvenient APIs for defining partials, you ask the language itself for more convenience - inline partials.

Sorry if I sound like I wildly try to double guess you. But the fact that you "don't see" something does not mean there is nothing - just that you don't see it. So do, arguably, the implementations you're fighting with in your quest for doing the real job.

The problem is the APIs, not the language syntax.

For example, when you say "Using lambdas to process another template seems like reinventing the wheel to me", I see the opportunity to encapsulate code chunks in clear and focused units. I see Mustache that sneaks in the territory of Liquid templates. I see Mustache able to render {{items}} just like Ruby on Rails renders <%= render @items %> (actually, @thelucid got the idea first).

All that is actually implemented in GRMustache, which is a Mustache library that allows you to write spec-compliant templates, but has been designed to help its users.

Anyway. Now that you can't use lambdas because they are unusable in the libraries you are using, and because inline templates are for sure easier to implement, and thus may qualify as a reasonable goal, how could they look like?

What about the = character for defining inline templates? It looks like a definition, doesn't it?

{{=person}}
    <a href=...
{{/}}

{{#democrats}}
    {{>person}}
{{/democrats}}

In order to help the implementors, we could say that inline templates have to be defined before their usage, in order to prevent a forced double parsing phase.

In case of multiple definitions, the last definition before usage wins:

{{=person}}
    ignored {{! 1 }}
{{/}}

{{=person}}
    <a href="..." {{! 2 }}
{{/}}

{{#democrats}}
    {{>person}} {{! renders partial 2 }}
{{/democrats}}

{{=person}}
    <a href="..." {{! 3 }}
{{/}}

{{#republicans}}
    {{>person}} {{! renders partial 3 }}
{{/republicans}}

Question: should an inline definition be superseded by a partial that is programmatically defined? Or the opposite?

In order to support Mustache implementations that can load templates and partials from a hierarchy of files and directories, allowing to define {{= shared/header }}...{{/}} could be a plus, allowing partials inside the shared folder to use {{> header}}. This would be consistent.

Some filesystem-based Mustache implementations also define absolute paths to partials ({{> a}} renders the sibling a, when {{> /a }} renders the root a). So it would be nice to allow the definition of {{= /person}} as well, in order to have {{> /person}} rendered in the same fashion in partials of arbitrary depth.

Do you see any more rules that should be written?

@ghost
Copy link

ghost commented Jan 18, 2013

Have to agree with @groue on this one, definitely something that could/should be handled with a lambda. Or, in the case of Tache, you can simply return a compiled (or uncompiled) template object from your view method for rendering. I think this would be a more worthy addition to the spec as it has far more applications than just inline partials, something like:

  • An implementation MUST allow compiled or uncompiled template instances to be returned from views for rendering.

@groue
Copy link

groue commented Jan 18, 2013

Or, in the case of Tache, you can simply return a compiled (or uncompiled) template object from your view method for rendering

Nice. Excellent convergence with GRMustache here :-)

id data = @{
    @"democrats": ...,
    @"republicans": ...,
    @"link": [GRMustacheTemplate templateFromString:@"<a href=\"/user/{{username}}\">{{name}}</a>" error:NULL],
};

NSString *rendering = [GRMustacheTemplate renderObject:data
                                          fromResource:@"Document"
                                                bundle:nil
                                                 error:NULL];

@groue
Copy link

groue commented Jan 18, 2013

Dynamic partials discussed in #49 can be achieved with exactly the same logic, actually. I guess it's the same in Tache:

id data = @{
    @"democrats": ...,
    @"republicans": ...,
    @"link": [GRMustacheTemplate templateFromResource:@"Link" bundle:nil error:NULL],
};

NSString *rendering = [GRMustacheTemplate renderObject:data
                                          fromResource:@"Document"
                                                bundle:nil
                                                 error:NULL];

@spudly
Copy link
Author

spudly commented Jan 18, 2013

Maybe I'm wrong (because I am not familiar enough with objective-c) but what you're doing in that code looks like what I'm already doing with partials... I have separate template files that I compile and pass to a render function. These are then rendered wherever I reference them in the main template and they inherit the variable scope when they do so. What I want to do is to define the partials in the main template file so I don't have to define them in a separate file and pass them in.

@ghost
Copy link

ghost commented Jan 18, 2013

@spudly I see your problem. I was going to suggest creating a generic inline_partial lambda that captures the content and saves it for later, however you wouldn't be able to store the contents by name for recall later.

This touches on the larger subject of helpers which Mustache is currently lacking. I'm talking about helpers in the Handlebars sense, for example you could handle your problem with a 'capture' helper in Handlebars:

{{! The following would capture the content into a 'captured' hash for later. }}

{{#capture "my_partial_name"}}
Some stuff I want to repeat
{{/capture}}

<h1>{{captured.my_partial_name}}</h1>
<footer>
  <p>{{captured.my_partial_name}}</p>
</footer>

I think something needs to be agreed upon when it comes to helper support in Mustache. The more I play with Handlebars, the more my helper envy grows. What are your thoughts @groue?

@groue
Copy link

groue commented Jan 18, 2013

@thelucid : this would require helpers/filters AND literal parsing.

@ghost
Copy link

ghost commented Jan 18, 2013

@groue Good point, although literals in Handlebars are only in the case of helpers, not a global change. They just get sent as arguments to the helper functions.

I'm starting to think that this could address many of the questions that crop up in this repo.

@groue
Copy link

groue commented Jan 19, 2013

@spudly

What I want

I suggest you quit this childish tone immediately.

https://github.com/mustache/spec/blob/master/specs/%7Elambdas.yml#L96 shows that Mustache 1.1.2 lambdas inherit the current context when rendering: lambdas are the answer of Mustache 1.1.2 to your problem.

Since the Mustache implementations you are using prevent you from using them, go and open issues in their repositories.

@thelucid

@groue Good point, although literals in Handlebars are only in the case of helpers, not a global change. They just get sent as arguments to the helper functions.

I'm starting to think that this could address many of the questions that crop up in this repo.

Could they? I suggest you gather link to all issues that are related to literals in a global "literal" issue. Maybe the case for them will be so strong we'll just jump on the wagon right away. Or maybe not :-)

@groue
Copy link

groue commented Jan 19, 2013

(I really should improve my English and stop "suggesting" like a machine gun :-)

@ghost
Copy link

ghost commented Jan 19, 2013

@groue I didn't sense a childish tone from @spudly in the slightest, he simply wants to solve a problem in his Mustache workflow which is fair enough.

I'm not suggesting that literals would solve many issues, I am suggesting that Handlebars style helpers (with basic literal support) would. You seem very quick to dismiss ideas unless they are your own.

@spudly
Copy link
Author

spudly commented Jan 19, 2013

@groue I hardly think I'm being childish. If anything you have a hostile
tone. Let's have a discussion, not an argument.

Thanks for pointing out that lambdas inherit variable scope. I was not
aware of that. Will look into it.

Even so, I don't see how lambdas are a solution to my problem. The solution
to my problem is partials. I can easily enough create a partial as a string
in my view and pass it in as a partial. That's really all I would have to
do. A better solution to my problem would be inline partials, because then
I don't have template code in my view!
On Jan 19, 2013 12:54 AM, "Gwendal Roué" notifications@github.com wrote:

@spudly https://github.com/spudly

What I want

I suggest you quit this childish tone immediately.

https://github.com/mustache/spec/blob/master/specs/%7Elambdas.yml#L96shows that Mustache 1.1.2 lambdas inherit the current context when
rendering: lambdas are the answer of Mustache 1.1.2 to your problem.

Since the Mustache implementations you are using prevent you from using
them, go and open issues in their repositories.

@thelucid https://github.com/thelucid

@groue https://github.com/groue Good point, although literals in
Handlebars are only in the case of helpers, not a global change. They just
get sent as arguments to the helper functions.

I'm starting to think that this could address many of the questions that
crop up in this repo.

Could they? I suggest you gather link to all issues that are related to
literals in a global "literal" issue. Maybe the case for them will be so
strong we'll just jump on the wagon right away. Or maybe not :-)


Reply to this email directly or view it on GitHubhttps://github.com//issues/63#issuecomment-12450983.

@ghost
Copy link

ghost commented Jan 19, 2013

@spudly Do you see how Handlebars style helpers would allow you to achieve this?

@spudly
Copy link
Author

spudly commented Jan 19, 2013

@jamie, yes I do see how helpers could solve this problem pretty easily,
although it would not be my preferred approach.
On Jan 19, 2013 7:15 AM, "Jamie Hill" notifications@github.com wrote:

@spudly https://github.com/spudly Do you see how Handlebars style
helpers would allow you to achieve this?


Reply to this email directly or view it on GitHubhttps://github.com//issues/63#issuecomment-12454113.

@ghost
Copy link

ghost commented Jan 19, 2013

@spudly My experience with this spec repo has been that generally new language additions aren't accepted straight off. Instead, the maintainers look for more generic ways of solving the same issues that keep the core of Mustache as lean as possible (a good example of this is my ticket on dynamic partials and partial collections. Whilst this can be frustrating, it does sometimes result in a more generic solution that benefits the whole of Mustache, not just the initial problem.

There are many issues that could benefit from a generic helper solution including your inline partial need, hence my suggestion. Given my experience on other tickets, I wouldn't hold out too much hope for getting inline partials added specifically, however if there is a more generic solution, this is more likely to be accepted (I feel your pain).

@groue
Copy link

groue commented Jan 21, 2013

@thelucid got the context right. Adding a feature each time somebody uses italics on want won't happen any time soon.

Many ideas have been proposed here. None has been explored thoroughly enough.

@spudly has been stuck on his inline partials, never telling how, for example, should their conflict with external partials be resolved (see above for the first unfinished exploration of his idea).

@thelucid had a few difficulties with a "recorder" helper, and started gazing at Handlebars. I think invoking Handlebars is far too early. Handlebars is not the magic wand that solves all problems. Instead, you could focus first on a pair of lambdas, one that "records" its section, another that "replays" it. Is it possible? What is required to avoid recompilation? Any nasty corner cases?

For the record, at the end of this comment is some GRMustache code that implements this record/replay pair without recompilation. Yes, a Mustache engine can do it.

One argument for record/replay pairs is that they do not conflict with external partials. Maybe this conflict is a false problem. I wish we knew better. This requires in-depth inline partials analysis.

If the idea of record/replay pairs remains solid after investigation, then we can explore the literals. I agree with @thelucid that it looks like literals look quite useful in conjunction with filters/helpers (see for instance groue/GRMustache#37, and another idea by @spudly: "variable defaults" #64, which used literals). Still I wish we had a better landscape of use cases that involve literals.

The record/replay pair with GRMustache:

template.mustache:

{{#record}}
  <a href="{{url}}">{{username}}</a>
{{/record}}
{{#democrats}}
  {{replay}}
{{/democrats}}
{{#republicans}}
  {{replay}}
{{/republicans}}

Rendering code:

__block GRMustacheTag *recordedTag = nil;
id data = @{
    @"democrats": ...
    @"republicans": ...
    @"record": [GRMustache renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) {
        // Record: save tag for later, and don't render anything.
        recordedTag = tag;
        return @"";
    }],
    @"replay": [GRMustache renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) {
        // Replay: render recorded tag.
        return [recordedTag renderContentWithContext:context HTMLSafe:HTMLSafe error:error];
    }],
};

NSString *rendering = [tempalte renderObject:data error:NULL];

@groue
Copy link

groue commented Jan 21, 2013

Given my experience on other tickets, I wouldn't hold out too much hope for getting inline partials added specifically, however if there is a more generic solution, this is more likely to be accepted (I feel your pain).

Given my own experience, the spec maintainers @pvande, @defunkt and @janl won't show their nose and won't update the spec. Our only solution is to find a reasonably good solution for use cases, and implement them right away without spec support, hoping other implementors will follow. This is the really sad reality. A spec with maintainers who moved on.

@ghost
Copy link

ghost commented Jan 21, 2013

@groue I would say that nested helpers are a bad idea in a template anyway as things could get messy pretty fast.

@ghost
Copy link

ghost commented Jan 21, 2013

@groue If you needed nested helpers, you could accomplish with block helpers i.e. {{#upcase}}{{helper thing}}{{/upcase}} or simply with methods i.e. {{helper thing.upcase}}

@groue
Copy link

groue commented Jan 21, 2013

Yes, or it can compile it only the first time

OK, that's better than nothing :-) That's still one extra compilation phase, since the record section has already been compiled when the record lambda is invoked.

That would work as expected.

For you it's expected that {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing?

I would say that nested helpers are a bad idea in a template anyway as things could get messy pretty fast.

Come on, are you like that? You a duck-typing guy? Again: GRMustache users do not open tickets saying "please allow me to compose helpers", or "please remove extra brackets and commas". Even less "please clean up the mess I've been messing up with using your messy library".

@groue
Copy link

groue commented Jan 21, 2013

@groue If you needed nested helpers, you could accomplish with block helpers i.e. {{#upcase}}{{helper thing}}{{/upcase}} or simply with methods i.e. {{helper thing.upcase}}

Sure. Or use a syntax that does not suck.

@ghost
Copy link

ghost commented Jan 21, 2013

OK, that's better than nothing :-) That's still one extra compilation phase, since the record section has already been compiled when the record lambda is invoked.

The lambda could receive the compiled tokens but the spec says that it should only receive text.

For you it's expected that {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing?

Yes, why would it? You are replaying one thing, capturing a new thing, then replaying that.

Come on, are you like that?

I'm not, but the thing I like about Mustache as it generally doesn't allow template authors to get in a mess. I can happily teach a designer the basics of Mustache, supply a JSON structure and he's unlikely to hang himself. With nested helpers, brackets etc. I see him trying all kinds of whacky stuff.

Sure. Or use a syntax that does not suck.

I don't see why you think {{helper thing.upcase}} sucks? Very succinct if you ask me... I've not seen anyone complain about the Handlebars helper syntax either.

@ghost
Copy link

ghost commented Jan 21, 2013

@ghost
Copy link

ghost commented Jan 21, 2013

@janl How do you feel about:

  • An implementation must allow compiled or uncompiled template instances to be returned from views for rendering.
  • An implementation must render {{items}} as {{#items}}{{.}}{{/items}} when items is a collection.
  • An implementation should allow objects to coerce themselves to a template instance or string for rendering.

These three rules solve Dynamic Partials (#49 and #54), Partial Collections (#54) and no doubt, many other situations. All three rules have been tried and tested in Tache and GRMustache.

Edit: Probably should go in another ticket but as we're discussing what should enter the spec here, felt like a good place to ask. I am happy to write the specs and submit a pull request.

@groue
Copy link

groue commented Jan 21, 2013

OK, that's better than nothing :-) That's still one extra compilation phase, since the record section has already been compiled when the record lambda is invoked.

The lambda could receive the compiled tokens but the spec says that it should only receive text.

@thelucid Pretty limiting, hu? What about taking another point of view, and saying "my lib must allow people to render spec-compliant templates", instead of "my lib must behave exactly, no more, no less, as described by the spec"?

For you it's expected that {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing?

Yes, why would it? You are replaying one thing, capturing a new thing, then replaying that.

I'm talking about two distinct renderings of the same entire template, provided with the same input data.

Come on, are you like that?

I'm not, but the thing I like about Mustache as it generally doesn't allow template authors to get in a mess. I can happily teach a designer the basics of Mustache, supply a JSON structure and he's unlikely to hang himself. With nested helpers, brackets etc. I see him trying all kinds of whacky stuff.

OK. Let's build a toy, then.

@groue handlebars-lang/handlebars.js#222

Thanks for the link. Let's look in details:

...For format tweaks...

{{ escape_javacript(uppercase(name)) }} is not about formatting. Let's look at another argument.

If you look in the issue tracker, you'll see that there are still some issues related to interactions between existing helper styles (like if and bindAttr), and every new piece of possible syntax dramatically increases the interaction surface area.

Oh. Handlebars is difficult to test. I feel sorry for them.

Perhaps some day, when the existing bindings are extremely solid and mature, we can look into adding new features. For now, if it is possible to implement something via computed properties (or other existing primitives), I'm going to lean heavily in favor of using those primitives over adding new syntax.

Oh. Handlebars is not mature enough. I feel sorry for them as well.

GRMustache system is very simple, and thus extremely well tested, and extremely solid.

Tags contain expressions. name, uppercase(name), last(people).name. Expressions have a value computed in the current context. The value is asked to render for the tag. Depending on the class of the value and the tag type, booleans will render or not mustache sections, strings will render themselves, render or ignore sections, etc. Library users can provide their own rendering code, and have access to the same APIs as the standard rendering objects (bools, strings, arrays, etc.). Very few concepts, well integrated, for maximum flexibility. Because GRMustache does not nanny its users.

@ghost
Copy link

ghost commented Jan 21, 2013

@thelucid Pretty limiting, hu? What about taking another point of view, and saying "my lib must allow people to render spec-compliant templates", instead of "my lib must behave exactly, no more, no less, as described by the spec"?

I'm running automated tests against the spec so if I want it to pass, it's the only option. Either that, or get the spec updated to allow tokens (or a template instance) to be passed to the lambda.

I'm talking about two distinct renderings of the same entire template, provided with the same input data.

Oh, I see. I guess you'd just define a reset! method that cleared the instance variable.

OK. Let's build a toy, then.

Or you could make it more concise for simple cases and only require parentheses for nested calls like Ruby i.e.

{{! standard }}
{{helper value1 value2}}

{{#block_helper value1 value2}}
...
{{/block_helper}}


{{! nested }}
{{helper upcase(value1) value2}}

{{#block_helper upcase(value1) value2}}
...
{{/block_helper}}


{{! with handlebars style optionals }}

{{helper value1 value2 optional_a=value3 optional_b=upcase(value4)

@groue
Copy link

groue commented Jan 21, 2013

I'm running automated tests against the spec so if I want it to pass, it's the only option. Either that, or get the spec updated to allow tokens (or a template instance) to be passed to the lambda.

Well, you have to pass the spec's tests. And your own for each of your additions. How do you think other implementors do, when they extend the language?

Oh, I see. I guess you'd just define a reset! method that cleared the instance variable.

Yes, I have exactly the same problem. This is an encapsulation issue we need to address. If we do not, the user will have to document his code with sentences like "Remember to call reset! before using the template". This would be a real mess, this time.

Or you could make it more concise for simple cases and only require parentheses for nested calls like Ruby i.e.

{{! standard }}
{{helper value1 value2}}

{{#block_helper value1 value2}}
...
{{/block_helper}}

{{! nested }}
{{helper upcase(value1) value2}}

{{#block_helper upcase(value1) value2}}
...
{{/block_helper}}

{{! with handlebars style optionals }}

{{helper value1 value2 optional_a=value3 optional_b=upcase(value4)

You're border-line dishonest this time :-) I'm sure one reason for which you don't like much the f(g(x)) syntax is the fact that the parser must be recursive, and is less easy to implement than a simple <identifier>[<white_space><identifier>]. And now parsing would become even more difficult, since your syntax for "nested helpers" introduces syntax inconsistency!

Let's have a look at your actual Ruby code in Tache... Yeah, you're like me. You always use parenthesis when methods have arguments, and never use them when methods have no argument. You don't like ambiguity. Neither do I:

{{ name }}
{{ uppercase(person.name) }}
{{# withPosition(items) }} {{position}} {{name}} {{/}}
{{# empty?(items) }} No items {{/}}

This is already implemented in GRMustache, and I don't want to switch to a poorer solution.

I haven't yet needed named arguments in my own templates, as Handlebars do - I'd take inspiration from Ruby on that topic, I guess.

@ghost
Copy link

ghost commented Jan 21, 2013

Well, you have to pass the spec's tests. And your own for each of your additions. How do you think other implementors do, when they extend the language?

Adding an additional argument would break the test I believe. Switching from text to tokens would definitely break the spec.

Yes, I have exactly the same problem. This is an encapsulation issue we need to address. If we do not, the user will have to document his code with sentences like "Remember to call reset! before using the template". This would be a real mess, this time.

I think that if the user is going to litter their views with instance variable, it should be up to them to clear them. We could always provide some kind of after_render method that get's invoked if present.

You're border-line dishonest this time :-)

You are very quick with your accusations ;-) I hadn't even given the complexity of parsing a thought, just prefer the Handlebars syntax for the majority of cases.

Let's have a look at your actual Ruby code in Tache... Yeah, you're like me. You always use parenthesis when methods have arguments, and never use them when methods have no argument. You don't like ambiguity. Neither do I

You've not seen my ERB templates, that is when I do leave off parentheses as it makes for much more readable templates e.g. <%= link_to "Somewhere", somewhere_path %>. I would much prefer being able to leave off the parentheses for helpers that aren't nested... I don't mind the comma's but 90% of the time helpers won't me nested and parens just add unnecessary noise.

Named args could be handled the Ruby way too e.g. {{link_to name, path, class: css_class}}. Guess we should get the opinion of the guys over at: #41

@jgonggrijp
Copy link
Member

For what it's worth, I like the following syntax for defining an inline partial most, which I copied nearly verbatim from @groue in #63 (comment):

{{=person}}
    <a href=...
{{/person}}

{{#democrats}}
    {{>person}}
{{/democrats}}

The only change I made is that I repeated the person name in the End Section tag. I believe that would be better for consistency.

I also like @groue's argument for using the = sigil: it looks like a definition. Although : would probably work as well, for the same reason.

Some considerations:

  • This could be another optional extension, so it could be included in a 1.x release.
  • Implementations that implement both inline partials and inheritance, ideally should also allow combining them. That is, the inline partial definition might contain Block tags and it may be expanded with Parent tags in order to override those blocks.
  • The inline partial definition as a whole should probably be considered standalone, so it doesn't leave blank lines after rendering. Indentation could be handled in the same way as I'm proposing for Block and Parent tags in Additional inheritance specs for block reindentation #131 and Proposal on how to specify indentation of blocks #130.
  • In order to not overcomplicate things, I suggest allowing each inline partial to have only a single definition. @groue suggested a "last definition wins" approach, but this feels a bit too stateful to my taste. What happens when users break the single definition rule can be left implementation-defined.
  • Also in order not to overcomplicate things, I suggest that inline partials can only be defined at the top scope of the template (so not inside sections, blocks, parents, etcetera). Again, it can be left implementation-defined what happens otherwise. (Note also that it can still be the other way round, e.g. a block appearing inside an inline partial. I can even imagine this being very useful.)
  • I suggest that in case of name conflicts, inline partials take precedence over external partials. If the author of the template with the inline partial is unaware of the external partial, then she obviously expects the inline partial to be expanded. If she is aware of the external partial, then she always has the option to rename the inline partial in case she wants to be able to use both.

@jgonggrijp
Copy link
Member

Following up on my previous comment: I just realized that the = sigil is already taken by the Change Delimiters tag. In that case I guess : remains as a "next best".

{{:person}}
    <a href=...
{{/person}}

{{#democrats}}
    {{>person}}
{{/democrats}}

@forrestli74
Copy link

Any updates? I need this feature for my project. Here is the use case:
I'm using mustache to write color theme for multiple apps. It's much easier to encapsulate one color theme in one template file, but for some color theme, it has a lot of repeated parts that can be eliminated by inline partials. (example)

@gasche
Copy link
Contributor

gasche commented Jul 11, 2022

Implementations that implement both inline partials and inheritance, ideally should also allow combining them.

I agree, I see exactly the same use-cases with inheritance partials / parent tag.
Should the definition say whether it is defining a closed partial (no block parameter) or a parent tag? (eg {{:>name}} vs. {{:<name}}.) Or do we consider that they live in the same namespace, as for external partials/parents? The latter sounds simpler.

In order to not overcomplicate things, I suggest allowing each inline partial to have only a single definition. @groue suggested a "last definition wins" approach, but this feels a bit too stateful to my taste.
[...]
I suggest that in case of name conflicts, inline partials take precedence over external partials.

@jgonggrijp There is a small contradiction between your two proposals here, you want to avoid definition-precedence rules for inline partials, but still define precedence rules between inline partials and external partials.

I think that your internal-vs-external conforms to the general intuition of "the nearest definition wins" (as in, say, the CSS cascade model): internal definitions are closer to the use-case so they take precedence. It should be possible to have a similar notion of lexical scope for internal definitions, and that would tell us what is the right behavior in presence of multiple declarations.

To reformulate: what is the "visibility" of a partial/parent definition?

  • the whole file, including what's before it in the file?
  • or maybe just what follows? (sounds nicer)
  • if we allow definitions inside sections, do they remain visible after the section end? (does not sound like a good idea)
  • if we use a partial after the definition, is the definition visible from within the partial? (it sounds like a bad idea to me)

Also in order not to overcomplicate things, I suggest that inline partials can only be defined at the top scope of the template

This is a simplification, but it's also a bit frustrating because it is nice/appealing/convenient to be allowed to define inline partials close to their use-sites, which may be at the end of a long section for example.

{{#long-section}}
  lots of stuff
  {{:foo}}
  {{#list1}}{{>foo}}{{/list1}}
  {{#list2}}{{>foo}}{{/list2}}
{{/long-section}}

On the other hand, there is something we must be careful about: because the syntax for parent tag does not explicitly list its paremeters (unlike, say, a lambda-function syntax in most programming languages), there is a risk of ambiguity if we define an inline parent within an inline parent:

{{:<outer-parent-tag}}
  {{$arg}}{{/arg}}
  {{:<inner-parent-tag}}
    {{$other-arg}}{{/other-arg}}
  {{/inner-parent-tag]]
  ... uses {{<inner-parent-tag}} ...
{{/outer-parent-tag}}

in this example, is other-arg a block parameter of inner-parent-tag or of outer-parent-tag?

I think the best solution in this case is to say that other-tag is an argument of the innermost inline definition, because in the case where we really wanted to render a second argument of outer-parent-tag there is an ugly workaround:

{{:<outer-parent-tag}}
  {{$arg}}{{/arg}}
  {{:>ugly-workaround}}
    # render the argument here
    {{$other-arg}}{{/other-arg}}
  {{/ugly-workaround}}
  {{:<inner-parent-tag}}
    {{>ugly-workaround}}
  {{/inner-parent-tag]]
  ... uses {{<inner-parent-tag}} ...
{{/outer-parent-tag}}

Note: in this example I had to explicitly mark which inline definitions correspond to closed partials and which correspond to parent tags, because otherwise it would quickly get fairly confusing:

{{:outer-parent-tag}}
  {{$arg}}{{/arg}}
  {{:ugly-workaround}}
    # render the argument here
    {{$other-arg}}{{/other-arg}}
  {{/ugly-workaround}}
  {{:inner-parent-tag}}
    {{>ugly-workaround}}
  {{/inner-parent-tag]]
  ... uses {{<inner-parent-tag}} ...
{{/outer-parent-tag}}

The fact that parent-inside-parent is a bit delicate while closed-inside-parent is not suggests that it's good to force people to be explicit about whether they are defining an inline parent or an inline closed partial.

@jgonggrijp
Copy link
Member

@lijiaqigreat

Any updates? I need this feature for my project.

This project, and the present discussion, is about writing a specification for the template language feature; it is not about implementing the feature. If this feature becomes part of the specification, that will not immediately help you, because you need an actual implementation.

Here is the use case:
I'm using mustache to write color theme for multiple apps. It's much easier to encapsulate one color theme in one template file, but for some color theme, it has a lot of repeated parts that can be eliminated by inline partials. (example)

It appears you are already using a Mustache implementation. I see some non-standard syntax, so maybe it already implements inline partials? Otherwise, you can use "regular" external partials; they provide all the same features, except that you have to define the partial in a separate template file instead of in the main template.

@jgonggrijp
Copy link
Member

I agree, I see exactly the same use-cases with inheritance partials / parent tag. Should the definition say whether it is defining a closed partial (no block parameter) or a parent tag? (eg {{:>name}} vs. {{:<name}}.) Or do we consider that they live in the same namespace, as for external partials/parents? The latter sounds simpler.

Yes, I would prefer the latter. According to the current spec, parents are exactly like partials, except that you can pass argument blocks (which necessitates the block end tag). External templates that are used as "plain" partials can still contain blocks, and external templates that don't contain any blocks can still be used as parents.

It would not only be the simplest, but also the most consistent, to apply the same rule to inline partials/parents. In fact, it is probably a bit of misnomer to call them inline "partials" or "parents"; we should call them inline templates instead (or maybe "inline subtemplates", or "nested templates", to emphasize the nesting relationship). Whether another template is interpolated as a partial or a parent is decided at the site of interpolation, not at the site of definition.

In order to not overcomplicate things, I suggest allowing each inline partial to have only a single definition. @groue suggested a "last definition wins" approach, but this feels a bit too stateful to my taste.
[...]
I suggest that in case of name conflicts, inline partials take precedence over external partials.

@jgonggrijp There is a small contradiction between your two proposals here, you want to avoid definition-precedence rules for inline partials, but still define precedence rules between inline partials and external partials.

I think that your internal-vs-external conforms to the general intuition of "the nearest definition wins" (as in, say, the CSS cascade model): internal definitions are closer to the use-case so they take precedence. It should be possible to have a similar notion of lexical scope for internal definitions, and that would tell us what is the right behavior in presence of multiple declarations.

I meant something else: I do not want to specify what should happen when multiple inline tempate definitions have the same name, because doing that just is not clean, can easily be avoided and should not be encouraged. I think that inline templates ideally also should not have the same name as pre-existing external templates, but since that is harder to avoid, specifying the precedence still seemed necessary. Note that the spec does not specifiy what to do when multiple external templates have the same name, either.

I agree that "innermost wins" is a good rule in general. However, I believe there is no added value in allowing inline templates to "shadow" other inline templates. The template author is in full control of the template file, so if she wants to alternate between different versions of an inline template, she can just use different names. I should add that I am trying to keep my implementation as small as possible, and inline template names being tied to particular scopes (or being redefined at text order like Change Delimiter) is not going to help with that mission.

To reformulate: what is the "visibility" of a partial/parent definition?

  • the whole file, including what's before it in the file?
  • or maybe just what follows? (sounds nicer)

I would prefer the first option. Having to enforce that the inline template is invisible before the point of definition would significantly complicate my implementation.

Some users will prefer to put all their inline templates at the top (bottom-up programmers), while others might want to put all of them at the bottom (top-down programmers). I don't think either order is inherently better than the other.

On the other hand, the bottom-up order (definition before use) is probably a bit more consistent with Change Delimiter. We could also specifiy that the template must be visible after the definition, but leave it to the implementation to decide whether it is also visible before the definition.

On the other other hand, external templates are unordered, so there is something to say for inline templates to be unordered as well. That would again speak in favor of them being visible throughout the containing template file.

  • if we allow definitions inside sections, do they remain visible after the section end? (does not sound like a good idea)

Again, I would prefer not to tie the inline template to a particular scope, in order to keep the implementation compact.

  • if we use a partial after the definition, is the definition visible from within the partial? (it sounds like a bad idea to me)

External templates can be recursive, so I think it would be most consistent if inline templates can be, too. It also seems easiest to implement, since inline templates can be made of exactly the same "stuff" as external templates in that case.

Also in order not to overcomplicate things, I suggest that inline partials can only be defined at the top scope of the template

This is a simplification, but it's also a bit frustrating because it is nice/appealing/convenient to be allowed to define inline partials close to their use-sites, which may be at the end of a long section for example.

{{#long-section}}
  lots of stuff
  {{:foo}}
  {{#list1}}{{>foo}}{{/list1}}
  {{#list2}}{{>foo}}{{/list2}}
{{/long-section}}

Granted. I am not necessarily opposed to allowing the definition to appear within a nested scope, but in that case I would still want to "hoist" the inline template to the top scope, for the reasons I already discussed above.

On the other hand, there is something we must be careful about: because the syntax for parent tag does not explicitly list its paremeters (unlike, say, a lambda-function syntax in most programming languages), there is a risk of ambiguity if we define an inline parent within an inline parent:

{{:<outer-parent-tag}}
  {{$arg}}{{/arg}}
  {{:<inner-parent-tag}}
    {{$other-arg}}{{/other-arg}}
  {{/inner-parent-tag]]
  ... uses {{<inner-parent-tag}} ...
{{/outer-parent-tag}}

in this example, is other-arg a block parameter of inner-parent-tag or of outer-parent-tag?

I think this is a non-issue. Reading the spec closely (and figuring out how to pass it 100%), I found that any argument blocks provided to a template from the outside must be passed implicitly to any nested parents:

spec/specs/~inheritance.yml

Lines 182 to 209 in b2aeb3c

- name: Recursion
desc: Recursion in inherited templates
data: {}
template: "{{<parent}}{{$foo}}override{{/foo}}{{/parent}}"
partials:
parent: "{{$foo}}default content{{/foo}} {{$bar}}{{<parent2}}{{/parent2}}{{/bar}}"
parent2: "{{$foo}}parent2 default content{{/foo}} {{<parent}}{{$bar}}don't recurse{{/bar}}{{/parent}}"
expected: "override override override don't recurse"
- name: Multi-level inheritance
desc: Top-level substitutions take precedence in multi-level inheritance
data: { }
template: "{{<parent}}{{$a}}c{{/a}}{{/parent}}"
partials:
parent: "{{<older}}{{$a}}p{{/a}}{{/older}}"
older: "{{<grandParent}}{{$a}}o{{/a}}{{/grandParent}}"
grandParent: "{{$a}}g{{/a}}"
expected: c
- name: Multi-level inheritance, no sub child
desc: Top-level substitutions take precedence in multi-level inheritance
data: { }
template: "{{<parent}}{{/parent}}"
partials:
parent: "{{<older}}{{$a}}p{{/a}}{{/older}}"
older: "{{<grandParent}}{{$a}}o{{/a}}{{/grandParent}}"
grandParent: "{{$a}}g{{/a}}"
expected: p

The outer template can also explicitly pass the block to the inner template, in which case the point becomes moot entirely:

{{:<outer-parent-tag}}
  {{$arg}}{{/arg}}
  {{:<inner-parent-tag}}
    {{$other-arg}}{{/other-arg}}
  {{/inner-parent-tag]]
  ... {{<inner-parent-tag}}{{$other-arg}}{{/other-arg}}{{/inner-parent-tag}} ...
{{/outer-parent-tag}}

Why on earth should inline subtemplates have their own inline sub-subtemplates in the first place, though? That, again, seems needlessly complicated to me. This could be another reason to allow them only at top scope, as I suggested before, or alternatively, just to disallow nesting them. I will demonstrate below why nesting inline templates adds no value.

I think the best solution in this case is to say that other-tag is an argument of the innermost inline definition,

I agree to this purely because of consistency. other-arg appears inside inner-parent-tag, so the former is a parameter of the latter.

because in the case where we really wanted to render a second argument of outer-parent-tag there is an ugly workaround:

{{:<outer-parent-tag}}
  {{$arg}}{{/arg}}
  {{:>ugly-workaround}}
    # render the argument here
    {{$other-arg}}{{/other-arg}}
  {{/ugly-workaround}}
  {{:<inner-parent-tag}}
    {{>ugly-workaround}}
  {{/inner-parent-tag]]
  ... uses {{<inner-parent-tag}} ...
{{/outer-parent-tag}}

I don't think that workaround should be necessary. The spec for inheritance in external templates allows multiple templates to have block parameters with the same name, so the same should be allowed for inline templates, as far as I'm concerned.

{{:<outer-parent-tag}}
    {{$arg}}{{/arg}}
    {{:<inner-parent-tag}}
        {{$other-arg}}{{/other-arg}}
    {{/inner-parent-tag}}
    {{<inner-parent-tag}}
        {{$other-arg}}{{/other-arg}}
    {{/inner-parent-tag}}
    {{! optionally, let us use the second argument in the outer inline template as well}}
    {{$other-arg}}{{/other-arg}}
{{/outer-parent-tag}}

The above template will have exactly the same semantics if we deforest it:

{{:<inner-parent-tag}}
    {{$other-arg}}{{/other-arg}}
{{/inner-parent-tag}}

{{:<outer-parent-tag}}
    {{$arg}}{{/arg}}
    {{<inner-parent-tag}}
        {{$other-arg}}{{/other-arg}}
    {{/inner-parent-tag}}
    {{! optionally, let us use the second argument in the outer inline template as well}}
    {{$other-arg}}{{/other-arg}}
{{/outer-parent-tag}}

Note: in this example I had to explicitly mark which inline definitions correspond to closed partials and which correspond to parent tags, because otherwise it would quickly get fairly confusing:

{{:outer-parent-tag}}
  {{$arg}}{{/arg}}
  {{:ugly-workaround}}
    # render the argument here
    {{$other-arg}}{{/other-arg}}
  {{/ugly-workaround}}
  {{:inner-parent-tag}}
    {{>ugly-workaround}}
  {{/inner-parent-tag]]
  ... uses {{<inner-parent-tag}} ...
{{/outer-parent-tag}}

The fact that parent-inside-parent is a bit delicate while closed-inside-parent is not suggests that it's good to force people to be explicit about whether they are defining an inline parent or an inline closed partial.

Honestly, despite the fact that the workaround you're describing is convoluted and unnecessary, I do not even find it that confusing.

As I wrote before, I believe that the distinction between partials and parents is purely at the site of interpolation ("in the eyes of the beholder", so to speak), not at the site of definition. Just {{:name}} rather than {{:<name}} and {{:>name}} should be enough, I think.

@groue
Copy link

groue commented Jul 11, 2022

(Hi folks. Would you please not quote my username @groue in your comments? I have long stopped working with Mustache, and I wish I would not be notified about threads started ~ 10 years ago. Thank you in advance!)

@jgonggrijp
Copy link
Member

Hi @groue. Please set the notification settings for this repository to "ignore". In that way, you will never be notified, not even if we mention you. Then you do not need to rely on the cooperation from us and future people who will not have seen your request, and we can continue mentioning you, which has a meaningful function for us because you were a major contributor in the past.

@groue
Copy link

groue commented Jul 11, 2022

Did that.

Still, consider that notifying people 10 years after their last message in a thread is something that you should avoid, so that people do not have to make such a radical setup change.

Consider that after I set to notification settings to "ignore", people who do intend to notify me through this repository won't be able to do so. Your actions have a negative effects on other people actions. I'm sure this was not your initial intention.

Your cooperation, as well as cooperation from future people, is something we all rely on, regardless of all the features Github brings in order to cope with notification noise.

Have a nice day!

@forrestli74
Copy link

If this feature becomes part of the specification, that will not immediately help you, because you need an actual implementation.

I'm aware. But I still would like to see this in the spec, so it's more likely than not to be in all implementations of mustache in the long run.

@gasche
Copy link
Contributor

gasche commented Jul 12, 2022

@jgonggrijp your suggestions to have inline definitions scope over the whole file makes sense to me. (For me it makes even more sense to allow them inside sections, and then scope them only to this section.) But we still have to specify how they interact with partials: are they visible from within partials called in the file?

Personally I would expect this to work:

{{:part1}}...{{/part1}}

{{:part2}}
 ...
 {{>part1}}
 ...
{{/part2}}

{{>part2}}

but should that work in the case where part2 is an external partial, rather than an inline partial?

@jgonggrijp
Copy link
Member

Before I engage in the discussion again: please forgive my confrontational style. I read back my previous post and realized that it could come across as slightly polemic, but this is not what I'm after. I consider us to be on the same team: we try to arrive at a spec that works well enough for all implementers.

@jgonggrijp your suggestions to have inline definitions scope over the whole file makes sense to me.

Glad to read that!

(For me it makes even more sense to allow them inside sections, and then scope them only to this section.)

Honestly, I can see the sense in that as well. I imagine a use case like the following:

{{:address}}Doctor {{name}}{{/address}}

Hi {{>address}}!

{{#promotion}}
    {{:address}}Professor {{name}}{{/address}}
    
    Congratulations, {{>address}}!
{{/promotion}}

Goodbye, {{>address}}.

With {name: "Who"}, I would agree that the following output would be the most sensible (or least surprising):

Hi Doctor Who!
    Congratulations, Professor Who!
Goodbye, Doctor Who.

However, it is sensibility at a cost: the added complexity of implementation. Also, looking at the example, I wonder: do both inline templates really need to have the same name? Why not name them differently? I think that would even be less ambiguous and easier to understand.

{{:addressDoctor}}Doctor {{name}}{{/addressDoctor}}

Hi {{>addressDoctor}}!

{{#promotion}}
    {{:addressProfessor}}Professor {{name}}{{/addressProfessor}}
    
    Congratulations, {{>addressProfessor}}!
{{/promotion}}

Goodbye, {{>addressDoctor}}.

Once we agree to name each inline template differently, we no longer need rules about how they might shadow each other. We can also leave the choice to users, whether they want to restrict themselves to using a particular inline template inside a particular scope or not. It becomes a matter of taste rather than a matter of necessity.

But we still have to specify how they interact with partials: are they visible from within partials called in the file?

Ah, I realize you asked this before and I misunderstood the question. Sorry about that.

Personally I would expect this to work:

{{:part1}}...{{/part1}}

{{:part2}}
 ...
 {{>part1}}
 ...
{{/part2}}

{{>part2}}

Agreed.

but should that work in the case where part2 is an external partial, rather than an inline partial?

I think no: I would expect inline templates to be private to the file where they are defined. Otherwise, it becomes intractable where "external" partials/parents come from. In the following example, the author of part4.mustache might make part2.mustache render something that the author of part2.mustache never intended:

{{!part1.mustache}}
The regular thing.

{{!part2.mustache}}
{{>part1}}

{{!part3.mustache (behaves as author of part2.mustache intended)}}
{{>part2}}

{{!part4.mustache ("hijacks" part2.mustache)}}
{{:part1}}
    Now for something completely different.
{{/part1}}
{{>part2}}

I would agree that there is a use case for "hackable" templates, but I think it should be opt-in, and blocks already address that need.

{{!part1.mustache}}
The regular thing.

{{!part2.mustache}}
{{$hackme}}{{>part1}}{{/hackme}}

{{!part3.mustache (uses part2.mustache without customization)}}
{{>part2}}

{{!part4.mustache (uses part2.mustache with customization)}}
{{:part1}}
    Now for something completely different.
{{/part1}}
{{<part2}}
    {{$hackme}}{{>part1}}{{/hackme}}
{{/part2}}

@jgonggrijp
Copy link
Member

This discussion continues in #162.

@mustache mustache locked and limited conversation to collaborators Nov 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants