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

Named Template Blocks #47

Closed
wants to merge 4 commits into from
Closed

Named Template Blocks #47

wants to merge 4 commits into from

Conversation

taras
Copy link

@taras taras commented Apr 13, 2015

RFC

Ideas are @krisselden's, I wrote it up to open the discussion.

Here is what that might look like,

```
{{#ember-table}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an alternative syntax.

<ember-table as |t|>
  <t.header as |name|>
    {{name}}
  </t.header>
  <t.cell as |value|>
    {{value}}
  </t.cell>
</ember-table>

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stef's suggestion looks very similar to the proposed nested helper syntax :-/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed that this RFC and #43 aim to resolve the same issue from different aspects. Is that what you refer to, @mixonic ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanpenner one of the goals of the proposal is to allow the user to overwrite a portion of the default layout. Does what you're suggesting allow for this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stef's suggestion looks very similar to the proposed nested helper syntax :-/

yes, i would like to unify these two things.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanpenner this is a different use case and a different RFC, yielded helpers are about the block placing the content. Named templates are about the parent controlling the rendering via its layout. We need to keep these things separate.

@technomage
Copy link

Stef's syntax is easier to follow in terms of 'where is this coming from'. The ^ strikes me as trying to save keystrokes, but at the cost of clarity. Explicit data/control flow is better when reading the template IMO.

@taras
Copy link
Author

taras commented Apr 13, 2015

@technomage ^ is syntax that's built into Handlebars. {{else}} is an alias to {{^}}.

@ef4
Copy link
Contributor

ef4 commented Apr 15, 2015

I strongly favor the block params solution over named blocks, for all the reason @stefanpenner mentioned.

I disagree that it handles a different case. Yielded helpers are general enough that users can actually implement their own named blocks when they need that:

{{#my-parent as |p|}}
  {{#p.contentFor "header"}}
    Put this in the header
  {{/p.contentFor}}
  {{#p.contentFor "footer"}}
    Put this in the footer.
  {{/p.contentFor}}
{{/my-parent}}

@taras
Copy link
Author

taras commented Apr 15, 2015

@ef4 can you please describe how your code example works or what it does? or show how my-parent would use the provided block in the layout?

@ef4
Copy link
Contributor

ef4 commented Apr 15, 2015

In my example, the user is overriding default blocks in the parent's layout, just like in the RFC. I'll pick apart a slightly different example, because I think I can make the syntax even nicer:

{{#my-parent as |customize|}}
  {{#customize "header"}}
    My customized header
  {{/customize}}
  Hello world
{{/my-parent}}

Once you have the ability to yield and pass helpers (or components, which is not so different), you don't need any other special support from the framework, and can do all of the rest in normal components. Which means less syntax for people to learn. One possible example for the parent's template would be:

{{#customizable-content use=header}}
  Default header content goes here.
{{/customizable-content}}
...other parent layout stuff...
{{yield customize}}
...other parent layout stuff...
{{#customizable-content use=footer}}
  Default footer content goes here
{{/customizable-content}}

customize is a helper defined by the parent. Whatever blocks you pass to it get set as properties on the parent. A critical step here is that a block is treated as just a normal value that can be passed around and ultimately bound into the DOM where you want it to go. This is not a big step -- under the hood they already work that way. In fact, we probably don't even need a separate type for "block", because it has the same API as a yieldable helper.

customizable-content is just a normal component. (We could make the syntax even better if we let components take positional arguments, eliminating the use=. That's a feature I definitely intend to implement for 2.0. But for now I'm showing normal component syntax.)

customizable-content's template would be:

{{#if use}}
  {{use}}
{{else}}
  {{yield}}
{{/if}}

So putting it all together, this example would output:

<div class="this-is-the-parent">
  My customized header
  ...other parent layout stuff...
  Hello world
  ...other parent layout stuff...
  Default footer content goes here
</div>

because the header has been overridden by the child, and the footer has not.

@krisselden
Copy link

@ef4 customize helper does not get executed unless you yield, this is ignoring the scoping and execution rules of htmlbars and glimmer. You'd have to hack around a ton of stuff in order to get this to work.

@krisselden
Copy link

There is another strategy I think we could take that still plays well with scoping rules that could solve both problems that I'm going to try to convince @mmun to help me out making the RFC since I'm not very good at writing this stuff up, but I also think is easier to do via html tags.

@rwjblue
Copy link
Member

rwjblue commented Jun 7, 2015

Can you transfer this into an issue so that we can continue to iterate on the right way forward? We have had a few discussions on this topic at the most recent face to face, and might have a path forward to achieve the intent.

@rwjblue rwjblue closed this Jun 7, 2015
@taras
Copy link
Author

taras commented Jun 7, 2015

What would you like me to copy into issue?

@jamesarosen
Copy link

I just came across what I think is a good use-case for this. It doesn't seem to be exactly covered by the discussion above, but it's quite close.

{{#filtered-list filter="Bob" items=users as |list|}}
  {{#list.empty}}
    No users matching {{list.filter}}
  {{/list.empty}}

  {{#list.item as |user|}}
    {{user.name}}
  {{/list.item}}
{{/filtered-list}}

@ef4
Copy link
Contributor

ef4 commented Jun 8, 2015

@jamesarosen that case can already be done pretty elegantly with the {{yield to="inverse"}} capability that is in 1.13.

{{#filtered-each filter=myFilter items=users as |user|}}
  {{user.name}}
{{else}}
  No users matching {{myFilter}}
{{/each-filtered-list}}

The template of the filtered-each component would look like:

{{#if matchingItems}}
  {{#each matchingItems as |item|}}
    {{yield item}}
  {{/each}}
{{else}}
  {{yield to="inverse"}}
{{/if}}

@jamesarosen
Copy link

@ef4 :)

chancancode pushed a commit that referenced this pull request Oct 27, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants