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

Syntax/Feature Need: Passable Blocks (as args / other?) #791

Open
NullVoxPopuli opened this issue Feb 1, 2022 · 3 comments
Open

Syntax/Feature Need: Passable Blocks (as args / other?) #791

NullVoxPopuli opened this issue Feb 1, 2022 · 3 comments
Assignees

Comments

@NullVoxPopuli
Copy link
Contributor

NullVoxPopuli commented Feb 1, 2022

context: #460 (comment)

The lack of this capability is a HUGE growing pain for the projects I'm working on and causes a lot of cruft and awkward APIs, and having this feature would simplify a ton of things.

For library / ui / design-system authors, having passable blocks would be an IMMENSE lift / relief -- and it feels very surprising that this wasn't talked about more during the original RFC (or maybe it was planned to be added later, and just no one has gotten around to it)

Related thoughts:

  • maybe there is a way to:
    • reference anything
    • pass anything
<SubComponent @myBlock={{:myBlock}}>
<:@myBlock>

</:@myBlock>

Example:

the first place this came up was our big table component wrapped with all the bells and whistles.
Here is a more generic, yet very related, example.

A table component

  • could yield to="header"
  • and yield to="body"
  • etc

A wrapper component cannot

  • optionally yield to="header" and also have that header block be within the table component's header block (and the same for any block).

Like,

<WrappedTable>
  <:header>
     <th></th>
  </:header>
</WrappedTable>
and
<WrappedTable />

are not possible APIs.

You can hack around this, by, in the implementation of WrappedTable, put an if block around the entirety of Table, but that's 1. terrible, and 2. does not scale past 1 named block.

I have 7 named blocks in Table component

In order to provide a wrapper table (with nice defaults, optional blocks, etc), I cannot fathom (other than actually doing the math) the amount of code it would take to if-stamement my way through implementing that.

@locks locks self-assigned this Feb 14, 2022
@NullVoxPopuli NullVoxPopuli changed the title Reminder to myself to write an RFC for passing named blocks as arguments Syntax/Feature Need: Passable Blocks (as args / other?) Jun 17, 2022
@wagenet
Copy link
Member

wagenet commented Jul 23, 2022

@locks status?

@NullVoxPopuli
Copy link
Contributor Author

Another potential syntax:

<SubComponent @:myBlock={{:myBlock}} />

This implies:

  • :myBlock is a value that can be passed
  • a new arg syntax @: implies named block values could be accessed from this.args
    • what would this value look like in JS? do we error upon access, since it's meant for the template?
    • this syntax also implies that we may have other @* notations in the future
  • setting a value to a @: argument means the same as if the component wrote out:
    <:myBlock>
      {{yield to="somethingElse"}}
    </:myBlock>
    with the exception that if a value passed to @: is falsey, the named block matching the identifier after the : is not present.

Alternatively (or maybe in addition to, because this is about to be way more typing than the above,
we could allow control-flow syntaxes outisde named-block space.

For example:

{{! the contents of "MyDemo" component }}
<ListItem @list={{@list}}>
  {{#if (has-block "media")}}
    <:media>
      {{yield to="media"}}
    </:media>
  {{/if}}
</ListItem>

e.g.: if <MyDemo> is rendered with a <:media> block, pass those contents to <ListItem>, otherwise render <ListItem> as if no :media block were used.

@sukima
Copy link

sukima commented Sep 10, 2024

I just ran into this today. I needed a way to use Ember/Glimmer components to render dynamic content that is generated from a nested data structure. By nature of nested data structures we needed recursion which is supported but the parent's block(s) were not transferable to the recursive children and so they became no-ops making the system only able to handle one level of nesting in the data.

Example

data = [
  { type: ‘text’, value: ‘This is a  },
  {
    type: ‘link’,
    children: [
      { type: ‘text’, value: ‘fancy link  },
      { type: ‘icon’ },
    ],
  },
  { type: ‘text’, value:  to render. },
];

With something like this:

<MyFancyComponent @nodes={{this.data}} as |type Content|>
  {{#if (eq type "link")}}
    <MyLink><Content /></MyLink>
  {{else if (eq type "icon")}}
    <MyIcon />
  {{/if}}
</MyFancyComponent>

But if you notice the data structure has nested nodes under children so when attempting to recurse you would normally have this:

{{yield
  currentNode.type
  (component MyFancyComponent nodes=currentNode.children)
}}

Which is unable to pass along the parent's yielded block thus rendering any child nodes rendering useless.

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

No branches or pull requests

4 participants