shannonmoeller / handlebars-layouts

Handlebars helpers which implement layout blocks similar to Jinja, Nunjucks (Swig), Pug (Jade), and Twig.
http://npm.im/handlebars-layouts
MIT License
361 stars 29 forks source link

#extend vs #embed #12

Closed shannonmoeller closed 9 years ago

shannonmoeller commented 9 years ago

Originally from @kflorence in #11:


Yeah, just thinking out loud here. Not suggesting that is the appropriate syntax by any means, just that there seems to be some shortcuts for certain use cases.

I think I'm still a little confused between {{#extend}} and {{#embed}} -- for example, in the following case which one should I be using?

layouts/default.hbs

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{title}}</title>
  </head>
  <body>
    <main class="main" role="main">
      {{{block "main"}}}
    </main>
  </body>
</html>

partials/component.hbs

<div class="component">
  {{{block "partials/component/content"}}}
</div>

views/component.hbs

{{#extend "layouts/default"}}
  {{#content "layouts/default/main"}}
    {{!-- embed or extend? --}}
    {{#embed "partials/component"}}
      {{#content "partials/component/content"}}
        foo bar
      {{/content}}
    {{/embed}}
  {{/content}}
{{/extend}}

I've named the content blocks kind of strangely because they seemed to be causing trouble if they shared the same names, not sure if that was my error or not.


It seems like only {{#embed}} works in that context, but it doesn't really match with how I interpret the docs (since "partials/component" does not extend any layout).

Also, there seems to be some weird stuff going on with the contexts. Assuming there is a top level "file" property in data:

{{#extend "layouts/component"}}
  {{#content "layouts/component/content"}}
    {{debug "one" ../file}}
    {{#embed "partials/component"}}
      {{#content "partials/component/content"}}
        {{debug "two" ../../../file}}
      {{/content}}
    {{/embed}}
  {{/content}}
{{/extend}}

Logs:

----------------------------------------
one
<File "components/buttons/index.html" <Buffer 7b 7b 23 65 78 74 65 6e 64 20 22 6c 61 79 6f 75 74 73 2f 63 6f 6d 70 6f 6e 65 6e 74 22 7d 7d 0a 20 20 7b 7b 23 63 6f 6e 74 65 6e 74 20 22 6c 61 79 6f 75 ...>>
----------------------------------------
----------------------------------------
two
<File "components/buttons/index.html" <Buffer 7b 7b 23 65 78 74 65 6e 64 20 22 6c 61 79 6f 75 74 73 2f 63 6f 6d 70 6f 6e 65 6e 74 22 7d 7d 0a 20 20 7b 7b 23 63 6f 6e 74 65 6e 74 20 22 6c 61 79 6f 75 ...>>
----------------------------------------

Where I would expect "two" to reference the file by ../../file instead of ../../../file

Apologies for slightly hijacking this thread :)

shannonmoeller commented 9 years ago

1) The handlebars-layouts helpers allow you to reason about templates the same way you would Classes in OOP, where:

// {{#extend}} is to
class Page extends Layout { }

// as {{#embed}} is to
new Partial();

I agree that this needs to be better documented.

2) If you're experiencing block name collisions between extend and embed, that's a bug as it's intended to work. I'll get right on that.

3) You need to do ../../../file because you've descended into two helpers, both extend and content. That's admittedly a bit clunky as the only valid contents of an extend is content blocks. I'll see if I can do anything about that.

kflorence commented 9 years ago

@shannonmoeller Cool. I'll see if I can confirm "2)" before you spend time on it.

kflorence commented 9 years ago

@shannonmoeller

{{!--
  layouts/component

  @extends layouts/default
  @param {String} title The page and component title
  @block content The main content block
--}}
{{#extend "layouts/default"}}
  {{#content "content"}}
    <h2>{{file.data.title}}</h2>
    {{{block "content"}}}
  {{/content}}
{{/extend}}

This causes an exception: RangeError: Maximum call stack size exceeded if there is a layout which extends another layout with a colliding block name (in this case "content").

shannonmoeller commented 9 years ago

Mad props for the doc block.

shannonmoeller commented 9 years ago

I've refactored the test cases to ensure that everything works the way I expect it to work. Check out ./tests/fixtures/embed.html for an example on how to use embed.

The test case you provided isn't intended to work. What you want to do is use mode="prepend":

{{!--
  layouts/component

  @extends layouts/default
  @param {String} title The page and component title
  @block content The main content block
--}}
{{#extend "layouts/default"}}
  {{#content "content" mode="prepend"}}
    <h2>{{file.data.title}}</h2>
  {{/content}}
{{/extend}}

Any template that extends from layouts/component is still able to append to, prepend to, or replace the content block:

{{#extend "layouts/component"}}
  {{#content "content" mode="append"}}
    <p>Moar stuff.</p>
  {{/content}}
{{/extend}}
kflorence commented 9 years ago

@shannonmoeller Interesting. Makes sense. Still, I think it should be noted somewhere in the docs that you can't have nested #content blocks with the same names.

shannonmoeller commented 9 years ago

Agreed. I'm planning to overhaul the docs of a lot of my stuff once I get my latest project far enough along: https://github.com/togajs/toga