-
-
Notifications
You must be signed in to change notification settings - Fork 407
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
Conditional blocks #735
Comments
We could probably implement this in a non-breaking way by providing a |
I think the core need here is the ability to pass optional blocks, rather than detecting whether or not the block has content. Something like: <MyComponent>
{{#if @someProp}}
<:my-block><:/my-block>
{{/if}}
</MyComponent> Detecting whether or not a block is actively showing content seems like a much trickier problem to me. |
@pzuraq Definitely agree. Differentiating factor there being the requirement of a named block in order to conditionally show, hence my initial suggestion, but I like optional blocks better. Not sure if helpful, but I wrote this example in Vue the other day to showcase my use-case, which was changing error state to an input field based on if anything was passed in an error slot. https://codesandbox.io/s/summer-http-osuil?file=/src/App.vue |
Yep, what @pzuraq said. As I explained on Discord the exact thing you asked for is not possible:
Conditional blocks, on the other hand, does not have that problem, and that’s the equivalent of what your vue example is doing. Well sort of. There is still the problem that we currently need to eagerly/statically know what blocks are passed before the component is invoked (they work very much like arguments), so there is still that problem, but perhaps we could try to be more lazy. But even then, we will still have to work out the timing and restrictions on evaluating the conditions. For example, can you use <MyComponent>
{{#if (gt (rand 0.5))}}
<:my-block><:/my-block>
{{/if}}
</MyComponent> |
In the meantime you could use contextual components to emulate this for your exact case. |
Just for thoroughness, this approach would look like this: my-component.hbs
.... stuff above
{{yield (hash
foo=(component 'my-component/foo' defaultArg='something')
bar=(component 'my-component/foo/bar' someArg=(eq @argA 2))
)}}
.... stuff below usage: <MyComponent as |stuff|>
<stuff.foo />
{{#if @condition}}
<stuff.bar>
content not shown at all -- down side is that you need another component
(located at app/components/my-component/foo/bar.hbs)
</stuff.bar>
{{/if}}
</MyComponent> |
I've been using React for a few months on a different project and even though it is pretty annoying to use compared to glimmer components (which are so nice to work with when not having to force workarounds), React doesn't have these basic issues that you would never expect any templating language to have. Even the workaround with contexual components has issues because you can't pass attributes through to them anymore, you need |
This problem arises every time you make composition wrapping a component which presentation depends of wether or not the consumer has-block. component.hbs <div class={{if (has-block "description") "some-class"}}>
<h1>{{@title}}</h1>
{{#if (has-block "description")}}
<p>{{yield to="description"}}</p>
{{/if}}
</div> composed-component.hbs <Component @title={{@title}} class="my-unique-logical-class">
<:description>
{{yield to="description"}}
</:description>
</Component> The underlaying component will always render a component.hbs {{#let (and (arg-or-default @hasDescriptionBlock true) (has-block "description")) as |hasDescriptionBlock|}}
<div class={{if hasDescriptionBlock "some-class"}}>
<h1>{{@title}}</h1>
{{#if hasDescriptionBlock}}
<p>{{yield to="description"}}</p>
{{/if}}
</div>
{{/let}} composed-component.hbs <Component @title={{@title}} @hasDescriptionBlock={{has-block "description"}} class="my-unique-logical-class">
<:description>
{{yield to="description"}}
</:description>
</Component> Basically we have an escape hatch with a boolean that the consumer can provide by any means, like if the actual final consumer has that particular block or not. EDIT: just found this comment that describes this too #460 (comment) |
@betocantu93 thoughts on being able to pass blocks as arguments? |
@NullVoxPopuli you mean like splatting/forwarding blocks instead of being explicit in a wrapping component? Base component <div class={{if (has-block "description") "some-class"}}>
<h1>{{@title}}</h1>
{{#if (has-block "description")}}
<p>{{yield to="description"}}</p>
{{/if}}
</div> Wrapper <Component
@title={{@title}}
class="my-unique-logical-class"
...blocks
/> I think that would be a great way to avoid having to deal with conditional blocks stuff, but I think there's still a valid use case when you want to enrich the block in a wrapping component context without it being called if the real consumer doesn't call it, the escape hatch boolean im my prev comment also helps to avoid the permutations explosion you mentioned here #460 (comment) <Component @title={{@title}} @hasDescriptionBlock={{has-block "description"}} class="my-unique-logical-class">
<:description>
<span {{mutation-observer onMutation=this.cleverness}}>{{yield to="description"}}</span>
</:description>
</Component> |
That still doesn't help that much because you won't always have the same name for the block. You should simply be able to use a block in an if statement, that's the only real solution (passing in blocks like above should be added as well though). |
Yeah, I agree, this solution is just future proof, since the condition will still evaluate to true/false if the ideal solution lands... |
I was thinking something like |
What would it take to actually get this to RFC? Is there a path forward? |
core team opinions / buy-in / acknowledgement / ideas?, I think? Personally, I'd like to go for the blocks as arguments approach, as it allows block forwarding, which is essential in wrapping / abstracting components which provide named blocks. |
@NullVoxPopuli so I understand that core team ideas at this point would be nice, but I don't think that's a necessity to move to RFC. When thing are nebulous it can actually be harder to get good feedback. If there were an RFC for this I can assure you that it will get reviewed and, if there's any promise in the idea, we'll work with you to get it to completion. |
makes sense -- I'll try to find some time during work to figure out an RFC for this. thanks! |
EDIT: unsure about this Maybe a good starting point would be a smaller RFC that only focused on conditional blocks, and (maybe) also loops? At least for us, that's the main thing lacking from named blocks. Conditional<MyComponent>
{{#if @someProp}}
<:my-block><:/my-block>
{{/if}}
</MyComponent> Loops<MyComponent>
{{#each @myList as |item|}}
<:my-row @item=item><:/my-row>
{{/if}}
</MyComponent> |
I think allow blocks to be passed as args would cover this (aside from looping, that one doesn't (yet?) Make sense to me?) |
EDIT: this may not make sense There may be different ways of solving this, and I'm not sure my idea is the best. But to me, if/else gating would seem more natural. If/else gating for blocksFrom a DSL perspective, gating blocks behind if/else would make more sense to me. Since we use if/else blocks in our HBS templates in general, it would be intuitive that they worked in this scenario too. Looping scenario<CheckboxSelect>
{{#each myList as |item|
<:option @value={{item.val}} />
{{/each}}
</CheckboxSelect>
<!-- current workaround -->
<CheckboxSelect as |Option|>
{{#each myList as |item|
<Option @value={{item.val}}>
{{/each}}
</CheckboxSelect>
|
I think you want contextual components instead of a named block. or a combination of. <CheckboxSelect>
<:options as |Option|>
{{! render the options in the specific block/slot where options go,
as layout is constrained (the primary use case for blocks)
}}
{{#each myList as |item|}}
<Option @value={{item.val}} />
{{/each}}
</:options>
</CheckboxSelect> and syntactically, if you allow |
@NullVoxPopuli Makes sense, I understand. Good points! |
Hello @NullVoxPopuli, I landed to this while searching solution for an issue. So, I though to wave at you. |
Currently if you have dynamic content inside a component block, if not shown,has-block
will still returntrue
for that block.It should be possible that empty blocks (excluding whitespace/invisible characters) returnfalse
upon callinghas-block
.Provide the ability to optionally pass named blocks to components
The text was updated successfully, but these errors were encountered: