From 0e570c80214e76e93856ad8008e6b8ca346c4481 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 00:36:52 -0800 Subject: [PATCH 01/16] initial draft of named blocks RFC --- text/0000-named-blocks.md | 156 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 text/0000-named-blocks.md diff --git a/text/0000-named-blocks.md b/text/0000-named-blocks.md new file mode 100644 index 0000000000..52418aef53 --- /dev/null +++ b/text/0000-named-blocks.md @@ -0,0 +1,156 @@ +- Start Date: 2016-12-19 +- RFC PR: (leave this empty) +- Ember Issue: (leave this empty) + +# Summary + +Allow yielding to multiple named blocks in an Ember component. + +# Motivation + +Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, +e.g. a drop down menu element (cf. `ember-power-select`). However, currently the interface +to the components abstraction consits of: + * any number of attributes (and optionally many positional params) + * a single passed block (and optionally a single passed inverse block) +You will note that you can pass many attributes or params through a component's interace, +but only a single block. I think this has led to a trend in how we design high-level, complex +components which have to be flexible to fit many use cases. We've started buiding components +that are more configurable than they are composable. + +The goal of both configurability and composability is to provide felxibility in how you go from +input to output over a collection of interfaces in a system. Gaining this desired flexibility +via configurability tends to lead to fewer interfaces but more parameters to those interfaces. +Composability usually means many, more atomic interfaces that fit together with less parameters. +Take the example of JS functions: you can perform complex logic on input by calling a single +functions that takes in the input plus a giant config object telling you how to handle the input, +or you can break the logic into multiple functions that call each other. Logic written in a single +function with many configuration parameters will always be less flexible, maintainable, and reuable +than logic contained in many atomic functions. + +In Ember components we achieve composability by passing blocks that call other components. +Configurability, on the other hand, is achieved by passing attributes and positonal parameters. +Flexibility in high-level Ember UI components is determined by the variability of rendered content +that can be achieved by a sinlge component. Limiting block passing to a single, un-named block means +we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple +passed blocks of content. This leads to things like conditionally rendering some content based on +passed attributes or parameters or conditionally yielding different block parameters. Yielding to +multiple named blocks would make the use of a lot of the configuration that's currently happeneing +unecessary and in turn encourage composition of components instead. + +# Detailed design + +To achieve yielding to more than one named block I propose adding the following builtin syntax +and semantics to components: + +**sample-invocation.hbs** +``` +{{#some-component}} + This is the default, un-named block +{{:block-a as |block|}} + This is block {{block}} +{{:block-b as |block|}} + This is block {{block}} +{{/some-component}} +``` + +**some-component.hbs** +``` +{{yield:block-b 'B'}} +{{yield}} +{{yield:block-a 'A'}} +``` + +**result** +``` +This is block B +This is the default, un-named block +This is block A +``` + +If the default block is ommitted, to ease developer ergonomics I propose the following +additional syntax: + + +**some-component.hbs** +``` +{{yield:block-b 'B'}} +{{yield:block-a 'A'}} +``` + +**sample-invocation.hbs** +``` +{{#some-component:block-a as |block|}} + This is block {{block}} +{{:block-b as |block|}} + This is block {{block}} +{{/some-component}} +``` + +**result** +``` +This is block B +This is block A +``` + + +# How We Teach This + +The proposed syntax and semantics is a logical continuation of the current Ember syntax +and semantics relating to blocks and yielding blocks. The `block` sections of components +can directly analgized to `else` sections in the existing syntax. The proposed syntax can +also easily be taught by simply extending the current Ember guides on components, as this +is merely the addition of syntax that adds opt-in functionality. + +# Drawbacks + +The proposal adds more syntax to component invocation so it increases general learning curve. +The proposed syntax also might face some implementation challenges since it probably goes deep +into glimmer internals of how yields work. It also sort of duplicates the limited functionality +provided by the current `yield` helper when called with the `to` attribute for yielding to the +inverse block. + +# Alternatives + +This RFC was inspired by and draws knowledge from [this unresolved RFC](https://github.com/emberjs/rfcs/pull/72) +for named yields and [this closed RFC](https://github.com/emberjs/rfcs/pull/43) for a similar feature ask. +Ultimately there was no consensus on a syntax that was both clear and easy to teach but also technically desirable. +The main criticism of the closed RFC was that the proposed syntax was dynamic and not statically analyzable. +A dynamic version of this RFC would also be possible, looking something like this: + +**sample-invocation.hbs** +``` +{{#some-component:block 'block-a' as |block|}} + This is block {{block}} +{{block 'block-b' as |block|}} + This is block {{block}} +{{/some-component}} +``` + +**some-component.hbs** +``` +{{yield to="block-b" 'B'}} +{{yield to="block-a" 'A'}} +``` + +**result** +``` +This is block B +This is block A +``` + +The dynamic alternative follows the current syntax and semantics around inverse blocks more closely, but since +yielding to inverse blocks is not even very well documented, that may not be as salient as the benefits +of static analyzability. + +The primary alternative for this is to just forgoe named blocks completely and rely on contextual components +for composability. However, I feel there are problems with flexibility in content composability that +contextual components can't solve that named blocks/yields do. + +# Unresolved questions + +Is the colon syntax readable? I tried pretty much every delimiter character, and settled on colon. +What is the technical feasibility as far as changes to Glimmer that would have to be made? +As [@mmun](https://github.com/mmun) mentioned in [the closed RFC](https://github.com/emberjs/rfcs/pull/43) multiple yields, could this be better +implemented as a handlebars language feature that implements passing blocks around, foregoing semantic/syntactic +interactions with yield altogether? \ No newline at end of file From de702947ef530bc2aa7e5a2586ff6c51222248c7 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 00:46:00 -0800 Subject: [PATCH 02/16] Tweaks --- text/0000-named-blocks.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/text/0000-named-blocks.md b/text/0000-named-blocks.md index 52418aef53..fb9654fa6f 100644 --- a/text/0000-named-blocks.md +++ b/text/0000-named-blocks.md @@ -8,6 +8,13 @@ Allow yielding to multiple named blocks in an Ember component. # Motivation +### Why now? + +Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea +since it was always a well-liked idea that just happened to never come up at the right time. + +### Why ever? + Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, e.g. a drop down menu element (cf. `ember-power-select`). However, currently the interface to the components abstraction consits of: @@ -61,7 +68,7 @@ and semantics to components: {{yield:block-a 'A'}} ``` -**result** +**Result** ``` This is block B This is the default, un-named block @@ -87,7 +94,7 @@ additional syntax: {{/some-component}} ``` -**result** +**Result** ``` This is block B This is block A @@ -97,10 +104,10 @@ This is block A # How We Teach This The proposed syntax and semantics is a logical continuation of the current Ember syntax -and semantics relating to blocks and yielding blocks. The `block` sections of components -can directly analgized to `else` sections in the existing syntax. The proposed syntax can -also easily be taught by simply extending the current Ember guides on components, as this -is merely the addition of syntax that adds opt-in functionality. +and semantics relating to blocks and yielding blocks. The "block" sections (`:block-b` in the example above) +of components can directly analgized to `else` sections in the current syntax for inverse blocks. +The proposed syntax can be easily taught by simply extending the current Ember guides on components, +as this is merely the addition of syntax that adds opt-in functionality. # Drawbacks @@ -133,7 +140,7 @@ A dynamic version of this RFC would also be possible, looking something like thi {{yield to="block-a" 'A'}} ``` -**result** +**Result** ``` This is block B This is block A @@ -149,8 +156,8 @@ contextual components can't solve that named blocks/yields do. # Unresolved questions -Is the colon syntax readable? I tried pretty much every delimiter character, and settled on colon. +Is the colon syntax readable? I tried pretty much every delimiter character and settled on colon. What is the technical feasibility as far as changes to Glimmer that would have to be made? -As [@mmun](https://github.com/mmun) mentioned in [the closed RFC](https://github.com/emberjs/rfcs/pull/43) multiple yields, could this be better +As [@mmun](https://github.com/mmun) mentioned in [the closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields, could this be better implemented as a handlebars language feature that implements passing blocks around, foregoing semantic/syntactic -interactions with yield altogether? \ No newline at end of file +interactions with yield altogether? What are the pros and cons to that route? From 64424fb651951eb5ff1b02c0a9aea881b67c4dab Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 01:04:00 -0800 Subject: [PATCH 03/16] Spellcheck and rename document --- ...cks.md => 0000-named-blocks-and-yields.md} | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) rename text/{0000-named-blocks.md => 0000-named-blocks-and-yields.md} (79%) diff --git a/text/0000-named-blocks.md b/text/0000-named-blocks-and-yields.md similarity index 79% rename from text/0000-named-blocks.md rename to text/0000-named-blocks-and-yields.md index fb9654fa6f..174bc4ec90 100644 --- a/text/0000-named-blocks.md +++ b/text/0000-named-blocks-and-yields.md @@ -17,37 +17,37 @@ since it was always a well-liked idea that just happened to never come up at the Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, e.g. a drop down menu element (cf. `ember-power-select`). However, currently the interface -to the components abstraction consits of: +to the components abstraction consists of: * any number of attributes (and optionally many positional params) * a single passed block (and optionally a single passed inverse block) -You will note that you can pass many attributes or params through a component's interace, +You will note that you can pass many attributes or params through a component's interface, but only a single block. I think this has led to a trend in how we design high-level, complex -components which have to be flexible to fit many use cases. We've started buiding components +components which have to be flexible to fit many use cases. We've started building components that are more configurable than they are composable. -The goal of both configurability and composability is to provide felxibility in how you go from +The goal of both configurability and composability is to provide flexibility in how you go from input to output over a collection of interfaces in a system. Gaining this desired flexibility via configurability tends to lead to fewer interfaces but more parameters to those interfaces. Composability usually means many, more atomic interfaces that fit together with less parameters. Take the example of JS functions: you can perform complex logic on input by calling a single functions that takes in the input plus a giant config object telling you how to handle the input, or you can break the logic into multiple functions that call each other. Logic written in a single -function with many configuration parameters will always be less flexible, maintainable, and reuable +function with many configuration parameters will always be less flexible, maintainable, and reusable than logic contained in many atomic functions. In Ember components we achieve composability by passing blocks that call other components. -Configurability, on the other hand, is achieved by passing attributes and positonal parameters. +Configurability, on the other hand, is achieved by passing attributes and positional parameters. Flexibility in high-level Ember UI components is determined by the variability of rendered content -that can be achieved by a sinlge component. Limiting block passing to a single, un-named block means +that can be achieved by a single component. Limiting block passing to a single, un-named block means we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple passed blocks of content. This leads to things like conditionally rendering some content based on passed attributes or parameters or conditionally yielding different block parameters. Yielding to -multiple named blocks would make the use of a lot of the configuration that's currently happeneing -unecessary and in turn encourage composition of components instead. +multiple named blocks would make the use of a lot of the configuration that's currently happening +unnecessary and in turn encourage composition of components instead. # Detailed design -To achieve yielding to more than one named block I propose adding the following builtin syntax +To achieve yielding to more than one named block I propose adding the following built-in syntax and semantics to components: **sample-invocation.hbs** @@ -75,7 +75,7 @@ This is the default, un-named block This is block A ``` -If the default block is ommitted, to ease developer ergonomics I propose the following +If the default block is omitted, to ease developer ergonomics I propose the following additional syntax: @@ -105,7 +105,7 @@ This is block A The proposed syntax and semantics is a logical continuation of the current Ember syntax and semantics relating to blocks and yielding blocks. The "block" sections (`:block-b` in the example above) -of components can directly analgized to `else` sections in the current syntax for inverse blocks. +of components can directly analogized to `else` sections in the current syntax for inverse blocks. The proposed syntax can be easily taught by simply extending the current Ember guides on components, as this is merely the addition of syntax that adds opt-in functionality. @@ -150,9 +150,15 @@ The dynamic alternative follows the current syntax and semantics around inverse yielding to inverse blocks is not even very well documented, that may not be as salient as the benefits of static analyzability. -The primary alternative for this is to just forgoe named blocks completely and rely on contextual components -for composability. However, I feel there are problems with flexibility in content composability that -contextual components can't solve that named blocks/yields do. +Another alternative is to automagically yield some sort of hash of named blocks to the primary block and then +invoke the named blocks within the main block. Something like this is described by [@runspired](https://github.com/runspired) in a [gist](https://gist.github.com/runspired/71bc9ee3a6dd0386fb23) he posted +in response to [the unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for named yields. + +The primary alternative for this is to just forgo named blocks completely and rely on contextual components +for composability. However, I feel there are problems with flexibility in content composability that that named +blocks/yields solve contextual components just can't. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) +wihtout actually implementing it was suggested by [foxnewsnetwork](https://github.com/foxnewsnetwork). However, the +simulation of named yields is arguably hard to teach/understand. # Unresolved questions From d49e70ea1158ccfeb9848be8f5efc8b40b569002 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 01:15:03 -0800 Subject: [PATCH 04/16] small tweaks to named yields rfc --- text/0000-named-blocks-and-yields.md | 32 +++++++++++++--------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index 174bc4ec90..f72e711ff6 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -16,7 +16,7 @@ since it was always a well-liked idea that just happened to never come up at the ### Why ever? Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, -e.g. a drop down menu element (cf. `ember-power-select`). However, currently the interface +e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). However, currently the interface to the components abstraction consists of: * any number of attributes (and optionally many positional params) * a single passed block (and optionally a single passed inverse block) @@ -50,6 +50,13 @@ unnecessary and in turn encourage composition of components instead. To achieve yielding to more than one named block I propose adding the following built-in syntax and semantics to components: +**some-component.hbs** +``` +{{yield:block-b 'B'}} +{{yield}} +{{yield:block-a 'A'}} +``` + **sample-invocation.hbs** ``` {{#some-component}} @@ -61,13 +68,6 @@ and semantics to components: {{/some-component}} ``` -**some-component.hbs** -``` -{{yield:block-b 'B'}} -{{yield}} -{{yield:block-a 'A'}} -``` - **Result** ``` This is block B @@ -78,7 +78,6 @@ This is block A If the default block is omitted, to ease developer ergonomics I propose the following additional syntax: - **some-component.hbs** ``` {{yield:block-b 'B'}} @@ -100,7 +99,6 @@ This is block B This is block A ``` - # How We Teach This The proposed syntax and semantics is a logical continuation of the current Ember syntax @@ -125,6 +123,12 @@ Ultimately there was no consensus on a syntax that was both clear and easy to te The main criticism of the closed RFC was that the proposed syntax was dynamic and not statically analyzable. A dynamic version of this RFC would also be possible, looking something like this: +**some-component.hbs** +``` +{{yield to="block-b" 'B'}} +{{yield to="block-a" 'A'}} +``` + **sample-invocation.hbs** ``` {{#some-component:block 'block-a' as |block|}} @@ -134,12 +138,6 @@ A dynamic version of this RFC would also be possible, looking something like thi {{/some-component}} ``` -**some-component.hbs** -``` -{{yield to="block-b" 'B'}} -{{yield to="block-a" 'A'}} -``` - **Result** ``` This is block B @@ -157,7 +155,7 @@ in response to [the unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for The primary alternative for this is to just forgo named blocks completely and rely on contextual components for composability. However, I feel there are problems with flexibility in content composability that that named blocks/yields solve contextual components just can't. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) -wihtout actually implementing it was suggested by [foxnewsnetwork](https://github.com/foxnewsnetwork). However, the +wihtout actually implementing it was suggested by [@foxnewsnetwork](https://github.com/foxnewsnetwork). However, the simulation of named yields is arguably hard to teach/understand. # Unresolved questions From 7cafaa93bb507db075de517282803448ba6cb415 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 01:40:33 -0800 Subject: [PATCH 05/16] Add code highlighting and indent examples --- text/0000-named-blocks-and-yields.md | 153 ++++++++++++++------------- 1 file changed, 79 insertions(+), 74 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index f72e711ff6..75bba20825 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -10,7 +10,7 @@ Allow yielding to multiple named blocks in an Ember component. ### Why now? -Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea +Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea since it was always a well-liked idea that just happened to never come up at the right time. ### Why ever? @@ -50,61 +50,61 @@ unnecessary and in turn encourage composition of components instead. To achieve yielding to more than one named block I propose adding the following built-in syntax and semantics to components: -**some-component.hbs** -``` -{{yield:block-b 'B'}} -{{yield}} -{{yield:block-a 'A'}} -``` - -**sample-invocation.hbs** -``` -{{#some-component}} - This is the default, un-named block -{{:block-a as |block|}} - This is block {{block}} -{{:block-b as |block|}} - This is block {{block}} -{{/some-component}} -``` - -**Result** -``` -This is block B -This is the default, un-named block -This is block A -``` +> **some-component.hbs** +> ```hbs +> {{yield:block-b 'B'}} +> {{yield}} +> {{yield:block-a 'A'}} +> ``` +> +> **sample-invocation.hbs** +> ```hbs +> {{#some-component}} +> This is the default, un-named block +> {{:block-a as |block|}} +> This is block {{block}} +> {{:block-b as |block|}} +> This is block {{block}} +> {{/some-component}} +> ``` +> +> **Result** +> ``` +> This is block B +> This is the default, un-named block +> This is block A +> ``` If the default block is omitted, to ease developer ergonomics I propose the following additional syntax: -**some-component.hbs** -``` -{{yield:block-b 'B'}} -{{yield:block-a 'A'}} -``` - -**sample-invocation.hbs** -``` -{{#some-component:block-a as |block|}} - This is block {{block}} -{{:block-b as |block|}} - This is block {{block}} -{{/some-component}} -``` - -**Result** -``` -This is block B -This is block A -``` +> **some-component.hbs** +> ```hbs +> {{yield:block-b 'B'}} +> {{yield:block-a 'A'}} +> ``` +> +> **sample-invocation.hbs** +> ```hbs +> {{#some-component:block-a as |block|}} +> This is block {{block}} +> {{:block-b as |block|}} +> This is block {{block}} +> {{/some-component}} +> ``` +> +> **Result** +> ``` +> This is block B +> This is block A +> ``` # How We Teach This The proposed syntax and semantics is a logical continuation of the current Ember syntax -and semantics relating to blocks and yielding blocks. The "block" sections (`:block-b` in the example above) +and semantics relating to blocks and yielding blocks. The "block" sections (`:block-b` in the example above) of components can directly analogized to `else` sections in the current syntax for inverse blocks. -The proposed syntax can be easily taught by simply extending the current Ember guides on components, +The proposed syntax can be easily taught by simply extending the current Ember guides on components, as this is merely the addition of syntax that adds opt-in functionality. # Drawbacks @@ -117,32 +117,37 @@ inverse block. # Alternatives -This RFC was inspired by and draws knowledge from [this unresolved RFC](https://github.com/emberjs/rfcs/pull/72) -for named yields and [this closed RFC](https://github.com/emberjs/rfcs/pull/43) for a similar feature ask. +### Past RFCs + +This proposal was inspired by and draws knowledge from [this unresolved RFC](https://github.com/emberjs/rfcs/pull/72) +for named yields and [this closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields. Ultimately there was no consensus on a syntax that was both clear and easy to teach but also technically desirable. -The main criticism of the closed RFC was that the proposed syntax was dynamic and not statically analyzable. -A dynamic version of this RFC would also be possible, looking something like this: - -**some-component.hbs** -``` -{{yield to="block-b" 'B'}} -{{yield to="block-a" 'A'}} -``` - -**sample-invocation.hbs** -``` -{{#some-component:block 'block-a' as |block|}} - This is block {{block}} -{{block 'block-b' as |block|}} - This is block {{block}} -{{/some-component}} -``` - -**Result** -``` -This is block B -This is block A -``` + +### Other alternatives + +The main criticism of [the closed RFC]([this closed RFC](https://github.com/emberjs/rfcs/pull/43)) was that the proposed syntax was dynamic and not statically analyzable. +A dynamic version of this proposal would also be possible, looking something like this: + +> **some-component.hbs** +> ```hbs +> {{yield to="block-b" 'B'}} +> {{yield to="block-a" 'A'}} +> ``` +> +> **sample-invocation.hbs** +> ```hbs +> {{#some-component:block 'block-a' as |block|}} +> This is block {{block}} +> {{block 'block-b' as |block|}} +> This is block {{block}} +> {{/some-component}} +> ``` +> +> **Result** +> ``` +> This is block B +> This is block A +> ``` The dynamic alternative follows the current syntax and semantics around inverse blocks more closely, but since yielding to inverse blocks is not even very well documented, that may not be as salient as the benefits @@ -153,8 +158,8 @@ invoke the named blocks within the main block. Something like this is described in response to [the unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for named yields. The primary alternative for this is to just forgo named blocks completely and rely on contextual components -for composability. However, I feel there are problems with flexibility in content composability that that named -blocks/yields solve contextual components just can't. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) +for composability. However, I feel there are problems with flexibility in content composability that that named +blocks/yields solve contextual components just can't. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) wihtout actually implementing it was suggested by [@foxnewsnetwork](https://github.com/foxnewsnetwork). However, the simulation of named yields is arguably hard to teach/understand. From 30b8fb383ae8b72068ad48f78891b568d67c7a9c Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 01:58:08 -0800 Subject: [PATCH 06/16] delete unnecessary filler words and add more drawbacks --- text/0000-named-blocks-and-yields.md | 43 +++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index 75bba20825..c84a977f5c 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -11,7 +11,7 @@ Allow yielding to multiple named blocks in an Ember component. ### Why now? Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea -since it was always a well-liked idea that just happened to never come up at the right time. +since it was always a well-liked that just happened to never come up at the right time. ### Why ever? @@ -20,6 +20,7 @@ e.g. a drop down menu element (cf. [ember-power-select](https://github.com/ciber to the components abstraction consists of: * any number of attributes (and optionally many positional params) * a single passed block (and optionally a single passed inverse block) + You will note that you can pass many attributes or params through a component's interface, but only a single block. I think this has led to a trend in how we design high-level, complex components which have to be flexible to fit many use cases. We've started building components @@ -111,9 +112,43 @@ as this is merely the addition of syntax that adds opt-in functionality. The proposal adds more syntax to component invocation so it increases general learning curve. The proposed syntax also might face some implementation challenges since it probably goes deep -into glimmer internals of how yields work. It also sort of duplicates the limited functionality +into glimmer internals of how yields work. + +The proposal conflicts with the current behavior of `else` inverse blocks. It sort of duplicates the limited functionality provided by the current `yield` helper when called with the `to` attribute for yielding to the -inverse block. +inverse block. If implemented it could create some ambiguity around yielding to a block named "else", for example: + +> **some-component.hbs** +> ```hbs +> {{yield "default"}} +> {{yield:else "inverse"}} +> ``` +> +> **sample-invocation.hbs** +> ```hbs +> {{#some-component as |block|}} +> This is block {{block}} +> {{:else as |block|}} +> This is block {{block}} +> {{/some-component}} +> ``` + +Compared to the current syntax: + +> **some-component.hbs** +> ```hbs +> {{yield "default"}} +> {{yield to="inverse" "inverse"}} +> ``` +> +> **sample-invocation.hbs** +> ```hbs +> {{#some-component as |block|}} +> This is block {{block}} +> {{else as |block|}} +> This is block {{block}} +> {{/some-component}} +> ``` # Alternatives @@ -125,7 +160,7 @@ Ultimately there was no consensus on a syntax that was both clear and easy to te ### Other alternatives -The main criticism of [the closed RFC]([this closed RFC](https://github.com/emberjs/rfcs/pull/43)) was that the proposed syntax was dynamic and not statically analyzable. +The main criticism of [the closed RFC](https://github.com/emberjs/rfcs/pull/43) was that the proposed syntax was dynamic and not statically analyzable. A dynamic version of this proposal would also be possible, looking something like this: > **some-component.hbs** From e5f860e67da4b62ebf8c89795da7ae1f61da3e18 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 02:13:35 -0800 Subject: [PATCH 07/16] Bullet-ify open questions --- text/0000-named-blocks-and-yields.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index c84a977f5c..c17d85833f 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -200,8 +200,8 @@ simulation of named yields is arguably hard to teach/understand. # Unresolved questions -Is the colon syntax readable? I tried pretty much every delimiter character and settled on colon. -What is the technical feasibility as far as changes to Glimmer that would have to be made? -As [@mmun](https://github.com/mmun) mentioned in [the closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields, could this be better +* Is the colon syntax readable? I tried pretty much every delimiter character and settled on colon. +* What is the technical feasibility as far as changes to Glimmer that would have to be made? +* As [@mmun](https://github.com/mmun) mentioned in [the closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields, could this be better implemented as a handlebars language feature that implements passing blocks around, foregoing semantic/syntactic interactions with yield altogether? What are the pros and cons to that route? From a29176ac40307dfcdcaff46c7d9f5f7f0c841c91 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 02:19:17 -0800 Subject: [PATCH 08/16] Grammar mistakes --- text/0000-named-blocks-and-yields.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index c17d85833f..0fdca2fe27 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -36,9 +36,9 @@ or you can break the logic into multiple functions that call each other. Logic w function with many configuration parameters will always be less flexible, maintainable, and reusable than logic contained in many atomic functions. -In Ember components we achieve composability by passing blocks that call other components. +In Ember components, we achieve composability by passing blocks that call other components. Configurability, on the other hand, is achieved by passing attributes and positional parameters. -Flexibility in high-level Ember UI components is determined by the variability of rendered content +Flexibility, in high-level Ember UI components, is determined by the variability of rendered content that can be achieved by a single component. Limiting block passing to a single, un-named block means we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple passed blocks of content. This leads to things like conditionally rendering some content based on From 985cc3e9218b89ef13c9227855843dfac3c8c226 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 03:07:53 -0800 Subject: [PATCH 09/16] More grammar cleanup --- text/0000-named-blocks-and-yields.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index 0fdca2fe27..cac3df6780 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -103,10 +103,11 @@ additional syntax: # How We Teach This The proposed syntax and semantics is a logical continuation of the current Ember syntax -and semantics relating to blocks and yielding blocks. The "block" sections (`:block-b` in the example above) -of components can directly analogized to `else` sections in the current syntax for inverse blocks. +and semantics relating to blocks and yielding blocks. "Block" sections (`:block-b` in the example above) +of components can be directly analogized to `else` sections in the current syntax for inverse blocks. + The proposed syntax can be easily taught by simply extending the current Ember guides on components, -as this is merely the addition of syntax that adds opt-in functionality. +as it is merely the addition of syntax that provides opt-in functionality. # Drawbacks From 705d326549d47285cf865a9f27724eda6d595b0d Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 10:28:20 -0800 Subject: [PATCH 10/16] I'm bad at grammar --- text/0000-named-blocks-and-yields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index cac3df6780..d266e6e88a 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -11,7 +11,7 @@ Allow yielding to multiple named blocks in an Ember component. ### Why now? Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea -since it was always a well-liked that just happened to never come up at the right time. +since it was always well-liked but just happened to never come up at the right time. ### Why ever? From 671a78665bd3bc243b7f97f5fbca6c0f17f57b40 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 10:41:29 -0800 Subject: [PATCH 11/16] Sentence structure bikeshedding --- text/0000-named-blocks-and-yields.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index d266e6e88a..71c0be6988 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -16,8 +16,9 @@ since it was always well-liked but just happened to never come up at the right t ### Why ever? Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, -e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). However, currently the interface -to the components abstraction consists of: +e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). Currently, the interface +to the component abstraction consists of: + * any number of attributes (and optionally many positional params) * a single passed block (and optionally a single passed inverse block) @@ -42,9 +43,9 @@ Flexibility, in high-level Ember UI components, is determined by the variability that can be achieved by a single component. Limiting block passing to a single, un-named block means we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple passed blocks of content. This leads to things like conditionally rendering some content based on -passed attributes or parameters or conditionally yielding different block parameters. Yielding to +passed attributes/parameters or conditionally yielding different block parameters. Yielding to multiple named blocks would make the use of a lot of the configuration that's currently happening -unnecessary and in turn encourage composition of components instead. +unnecessary and, in turn, encourage composition of components instead. # Detailed design @@ -103,7 +104,7 @@ additional syntax: # How We Teach This The proposed syntax and semantics is a logical continuation of the current Ember syntax -and semantics relating to blocks and yielding blocks. "Block" sections (`:block-b` in the example above) +and semantics relating to blocks and yielding to blocks. "Block" sections (`:block-b` in the example above) of components can be directly analogized to `else` sections in the current syntax for inverse blocks. The proposed syntax can be easily taught by simply extending the current Ember guides on components, @@ -194,10 +195,8 @@ invoke the named blocks within the main block. Something like this is described in response to [the unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for named yields. The primary alternative for this is to just forgo named blocks completely and rely on contextual components -for composability. However, I feel there are problems with flexibility in content composability that that named -blocks/yields solve contextual components just can't. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) -wihtout actually implementing it was suggested by [@foxnewsnetwork](https://github.com/foxnewsnetwork). However, the -simulation of named yields is arguably hard to teach/understand. +for composability. However, I feel that named blocks/yields solve problems with flexibility in content composability +that contextual components just cannot. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) wihtout actually implementing it was suggested by [@foxnewsnetwork](https://github.com/foxnewsnetwork). However, this simulation of named yields is arguably hard to teach/understand. # Unresolved questions From 6733ad45e94d407d1b5cf771c89893445b147db8 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 12:58:02 -0800 Subject: [PATCH 12/16] Add changes to proposal Add changes to proposal suggested by @bendemboski and @simonihmig --- text/0000-named-blocks-and-yields.md | 162 +++++++++++++++++---------- 1 file changed, 101 insertions(+), 61 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index 71c0be6988..1488c9606e 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -10,47 +10,24 @@ Allow yielding to multiple named blocks in an Ember component. ### Why now? -Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea -since it was always well-liked but just happened to never come up at the right time. +Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea since it was always well-liked but just happened to never come up at the right time. ### Why ever? -Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, -e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). Currently, the interface -to the component abstraction consists of: +Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). Currently, the interface to the component abstraction consists of: * any number of attributes (and optionally many positional params) * a single passed block (and optionally a single passed inverse block) -You will note that you can pass many attributes or params through a component's interface, -but only a single block. I think this has led to a trend in how we design high-level, complex -components which have to be flexible to fit many use cases. We've started building components -that are more configurable than they are composable. - -The goal of both configurability and composability is to provide flexibility in how you go from -input to output over a collection of interfaces in a system. Gaining this desired flexibility -via configurability tends to lead to fewer interfaces but more parameters to those interfaces. -Composability usually means many, more atomic interfaces that fit together with less parameters. -Take the example of JS functions: you can perform complex logic on input by calling a single -functions that takes in the input plus a giant config object telling you how to handle the input, -or you can break the logic into multiple functions that call each other. Logic written in a single -function with many configuration parameters will always be less flexible, maintainable, and reusable -than logic contained in many atomic functions. - -In Ember components, we achieve composability by passing blocks that call other components. -Configurability, on the other hand, is achieved by passing attributes and positional parameters. -Flexibility, in high-level Ember UI components, is determined by the variability of rendered content -that can be achieved by a single component. Limiting block passing to a single, un-named block means -we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple -passed blocks of content. This leads to things like conditionally rendering some content based on -passed attributes/parameters or conditionally yielding different block parameters. Yielding to -multiple named blocks would make the use of a lot of the configuration that's currently happening -unnecessary and, in turn, encourage composition of components instead. +You will note that you can pass many attributes or params through a component's interface, but only a single block. I think this has led to a trend in how we design high-level, complex components which have to be flexible to fit many use cases. We've started building components that are more configurable than they are composable. + +The goal of both configurability and composability is to provide flexibility in how you go from input to output over a collection of interfaces in a system. Gaining this desired flexibility via configurability tends to lead to fewer interfaces but more parameters to those interfaces. Composability usually means many, more atomic interfaces that fit together with less parameters. Take the example of JS functions: you can perform complex logic on input by calling a single functions that takes in the input plus a giant config object telling you how to handle the input, or you can break the logic into multiple functions that call each other. Logic written in a single function with many configuration parameters will always be less flexible, maintainable, and reusable than logic contained in many atomic functions. + +In Ember components, we achieve composability by passing blocks that call other components. Configurability, on the other hand, is achieved by passing attributes and positional parameters. Flexibility, in high-level Ember UI components, is determined by the variability of rendered content that can be achieved by a single component. Limiting block passing to a single, un-named block means we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple passed blocks of content. This leads to things like conditionally rendering some content based on passed attributes/parameters or conditionally yielding different block parameters. Yielding to multiple named blocks would make the use of a lot of the configuration that's currently happening unnecessary and, in turn, encourage composition of components instead. # Detailed design -To achieve yielding to more than one named block I propose adding the following built-in syntax -and semantics to components: +To achieve yielding to more than one named block I propose adding the following built-in syntax and semantics to components: > **some-component.hbs** > ```hbs @@ -101,24 +78,59 @@ additional syntax: > This is block A > ``` +We would also need to include a corresponding continuation of the `hasBlock` property syntax currently available in component templates (credit goes to [@bendemboski](https://github.com/bendemboski) for this addition): + +> **some-component.hbs** +> ```hbs +> {{#if hasBlock:block-a}} +> {{yield:block-a 'A'}} +> {{/if}} +> HI +> {{#if hasBlock}} +> {{yield 'Default'}} +> {{/if}} +> ``` +> +> **sample-invocation-with-a.hbs** +> ```hbs +> {{#some-component as |block|}} +> This is block {{block}} +> {{:block-a as |block|}} +> This is block {{block}} +> {{/some-component}} +> ``` +> +> **Result with `block-a`** +> ``` +> This is block A +> HI +> This is block Default +> ``` +> +> **sample-invocation-without-a.hbs** +> ```hbs +> {{#some-component as |block|}} +> This is block {{block}} +> {{/some-component}} +> ``` +> +> **Result without `block-a`** +> ``` +> HI +> This is block Default +> ``` + # How We Teach This -The proposed syntax and semantics is a logical continuation of the current Ember syntax -and semantics relating to blocks and yielding to blocks. "Block" sections (`:block-b` in the example above) -of components can be directly analogized to `else` sections in the current syntax for inverse blocks. +The proposed syntax and semantics is a logical continuation of the current Ember syntax and semantics relating to blocks and yielding to blocks. "Block" sections (`:block-b` in the example above) of components can be directly analogized to `else` sections in the current syntax for inverse blocks. -The proposed syntax can be easily taught by simply extending the current Ember guides on components, -as it is merely the addition of syntax that provides opt-in functionality. +The proposed syntax can be easily taught by simply extending the current Ember guides on components, as it is merely the addition of syntax that provides opt-in functionality. # Drawbacks -The proposal adds more syntax to component invocation so it increases general learning curve. -The proposed syntax also might face some implementation challenges since it probably goes deep -into glimmer internals of how yields work. +The proposal adds more syntax to component invocation so it increases general learning curve. The proposed syntax also might face some implementation challenges since it probably goes deep into glimmer internals of how yields work. -The proposal conflicts with the current behavior of `else` inverse blocks. It sort of duplicates the limited functionality -provided by the current `yield` helper when called with the `to` attribute for yielding to the -inverse block. If implemented it could create some ambiguity around yielding to a block named "else", for example: +The proposal conflicts with the current behavior of `else` inverse blocks. It sort of duplicates the limited functionality provided by the current `yield` helper when called with the `to` attribute for yielding to the inverse block. If implemented it could create some ambiguity around yielding to a block named "else", for example: > **some-component.hbs** > ```hbs @@ -156,14 +168,11 @@ Compared to the current syntax: ### Past RFCs -This proposal was inspired by and draws knowledge from [this unresolved RFC](https://github.com/emberjs/rfcs/pull/72) -for named yields and [this closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields. -Ultimately there was no consensus on a syntax that was both clear and easy to teach but also technically desirable. +This proposal was inspired by and draws knowledge from [this unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for named yields and [this closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields. Ultimately there was no consensus on a syntax that was both clear and easy to teach but also technically desirable. -### Other alternatives +### Implementation alternatives -The main criticism of [the closed RFC](https://github.com/emberjs/rfcs/pull/43) was that the proposed syntax was dynamic and not statically analyzable. -A dynamic version of this proposal would also be possible, looking something like this: +The main criticism of [the closed RFC](https://github.com/emberjs/rfcs/pull/43) was that the proposed syntax was dynamic and not statically analyzable. A dynamic version of this proposal would also be possible, looking something like this: > **some-component.hbs** > ```hbs @@ -186,22 +195,53 @@ A dynamic version of this proposal would also be possible, looking something lik > This is block A > ``` -The dynamic alternative follows the current syntax and semantics around inverse blocks more closely, but since -yielding to inverse blocks is not even very well documented, that may not be as salient as the benefits -of static analyzability. +The dynamic alternative follows the current syntax and semantics around inverse blocks more closely, but since yielding to inverse blocks is not even very well documented, that may not be as salient as the benefits of static analyzability. + +Another alternative is to automagically yield some sort of hash of named blocks to the primary block and then invoke the named blocks within the main block. Something like this is described by [@runspired](https://github.com/runspired) in a [gist](https://gist.github.com/runspired/71bc9ee3a6dd0386fb23) he posted in response to [the unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for named yields. + +The primary alternative for this is to just forgo named blocks completely and rely on contextual components for composability. However, I feel that named blocks/yields solve problems with flexibility in content composability that contextual components just cannot. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) wihtout actually implementing it was suggested by [@foxnewsnetwork](https://github.com/foxnewsnetwork). However, this simulation of named yields is arguably hard to teach/understand. + +### Syntax alternatives -Another alternative is to automagically yield some sort of hash of named blocks to the primary block and then -invoke the named blocks within the main block. Something like this is described by [@runspired](https://github.com/runspired) in a [gist](https://gist.github.com/runspired/71bc9ee3a6dd0386fb23) he posted -in response to [the unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for named yields. +Some people (including me) are having doubts about the `:` delimiter syntax for named block and yield invocation. I was also considering `@` or `::`, as previewed below. + +With `@`: +> **some-component-at.hbs** +> ```hbs +> {{yield@block-b 'B'}} +> {{yield@block-a 'A'}} +> ``` +> +> **sample-invocation-at-syntax.hbs** +> ```hbs +> {{#some-component-at@block-a as |block|}} +> This is block {{block}} +> {{@block-b as |block|}} +> This is block {{block}} +> {{/some-component-at}} +> ``` +> -The primary alternative for this is to just forgo named blocks completely and rely on contextual components -for composability. However, I feel that named blocks/yields solve problems with flexibility in content composability -that contextual components just cannot. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) wihtout actually implementing it was suggested by [@foxnewsnetwork](https://github.com/foxnewsnetwork). However, this simulation of named yields is arguably hard to teach/understand. +With `::`: +> **some-component-double.hbs** +> ```hbs +> {{yield::block-b 'B'}} +> {{yield::block-a 'A'}} +> ``` +> +> **sample-invocation-double-syntax.hbs** +> ```hbs +> {{#some-component-double::block-a as |block|}} +> This is block {{block}} +> {{::block-b as |block|}} +> This is block {{block}} +> {{/some-component-double}} +> ``` # Unresolved questions -* Is the colon syntax readable? I tried pretty much every delimiter character and settled on colon. +* What interaction (if any) would this or should this have with slots in the Web Components spec? +* Should arbitrary ordering with the default block be allowed instead of always defining it first if it exists (cf. [@bendemboski](https://github.com/bendemboski)'s third point in [this comment](https://github.com/emberjs/rfcs/pull/193#issuecomment-268046073))? * What is the technical feasibility as far as changes to Glimmer that would have to be made? -* As [@mmun](https://github.com/mmun) mentioned in [the closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields, could this be better -implemented as a handlebars language feature that implements passing blocks around, foregoing semantic/syntactic -interactions with yield altogether? What are the pros and cons to that route? +* As [@mmun](https://github.com/mmun) mentioned in [the closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields, could this be better implemented as a handlebars language feature that implements passing blocks around, foregoing semantic/syntactic interactions with yield altogether? What are the pros and cons to that route? +* Is the colon syntax readable? I tried pretty much every delimiter character and settled on colon. From 1d28885a14ba61bd18416c3433a69564f2e4a8dc Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 16:04:08 -0800 Subject: [PATCH 13/16] Hide ancillary text and code blocks Hide ancillary code blocks and large explanatory paragraphs by default to increase readability of RFC --- text/0000-named-blocks-and-yields.md | 235 +++++++++++++++------------ 1 file changed, 135 insertions(+), 100 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index 1488c9606e..604fb86deb 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -8,22 +8,27 @@ Allow yielding to multiple named blocks in an Ember component. # Motivation -### Why now? +**Why now?** Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea since it was always well-liked but just happened to never come up at the right time. -### Why ever? +
+ + **Why ever?** + +

+ Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). Currently, the interface to the component abstraction consists of: -Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). Currently, the interface to the component abstraction consists of: + * any number of attributes (and optionally many positional params) + * a single passed block (and optionally a single passed inverse block) - * any number of attributes (and optionally many positional params) - * a single passed block (and optionally a single passed inverse block) + You will note that you can pass many attributes or params through a component's interface, but only a single block. I think this has led to a trend in how we design high-level, complex components which have to be flexible to fit many use cases. We've started building components that are more configurable than they are composable. -You will note that you can pass many attributes or params through a component's interface, but only a single block. I think this has led to a trend in how we design high-level, complex components which have to be flexible to fit many use cases. We've started building components that are more configurable than they are composable. + The goal of both configurability and composability is to provide flexibility in how you go from input to output over a collection of interfaces in a system. Gaining this desired flexibility via configurability tends to lead to fewer interfaces but more parameters to those interfaces. Composability usually means many, more atomic interfaces that fit together with less parameters. Take the example of JS functions: you can perform complex logic on input by calling a single functions that takes in the input plus a giant config object telling you how to handle the input, or you can break the logic into multiple functions that call each other. Logic written in a single function with many configuration parameters will always be less flexible, maintainable, and reusable than logic contained in many atomic functions. -The goal of both configurability and composability is to provide flexibility in how you go from input to output over a collection of interfaces in a system. Gaining this desired flexibility via configurability tends to lead to fewer interfaces but more parameters to those interfaces. Composability usually means many, more atomic interfaces that fit together with less parameters. Take the example of JS functions: you can perform complex logic on input by calling a single functions that takes in the input plus a giant config object telling you how to handle the input, or you can break the logic into multiple functions that call each other. Logic written in a single function with many configuration parameters will always be less flexible, maintainable, and reusable than logic contained in many atomic functions. - -In Ember components, we achieve composability by passing blocks that call other components. Configurability, on the other hand, is achieved by passing attributes and positional parameters. Flexibility, in high-level Ember UI components, is determined by the variability of rendered content that can be achieved by a single component. Limiting block passing to a single, un-named block means we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple passed blocks of content. This leads to things like conditionally rendering some content based on passed attributes/parameters or conditionally yielding different block parameters. Yielding to multiple named blocks would make the use of a lot of the configuration that's currently happening unnecessary and, in turn, encourage composition of components instead. + In Ember components, we achieve composability by passing blocks that call other components. Configurability, on the other hand, is achieved by passing attributes and positional parameters. Flexibility, in high-level Ember UI components, is determined by the variability of rendered content that can be achieved by a single component. Limiting block passing to a single, un-named block means we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple passed blocks of content. This leads to things like conditionally rendering some content based on passed attributes/parameters or conditionally yielding different block parameters. Yielding to multiple named blocks would make the use of a lot of the configuration that's currently happening unnecessary and, in turn, encourage composition of components instead. +

+
# Detailed design @@ -130,70 +135,89 @@ The proposed syntax can be easily taught by simply extending the current Ember g The proposal adds more syntax to component invocation so it increases general learning curve. The proposed syntax also might face some implementation challenges since it probably goes deep into glimmer internals of how yields work. -The proposal conflicts with the current behavior of `else` inverse blocks. It sort of duplicates the limited functionality provided by the current `yield` helper when called with the `to` attribute for yielding to the inverse block. If implemented it could create some ambiguity around yielding to a block named "else", for example: - -> **some-component.hbs** -> ```hbs -> {{yield "default"}} -> {{yield:else "inverse"}} -> ``` -> -> **sample-invocation.hbs** -> ```hbs -> {{#some-component as |block|}} -> This is block {{block}} -> {{:else as |block|}} -> This is block {{block}} -> {{/some-component}} -> ``` - -Compared to the current syntax: - -> **some-component.hbs** -> ```hbs -> {{yield "default"}} -> {{yield to="inverse" "inverse"}} -> ``` -> -> **sample-invocation.hbs** -> ```hbs -> {{#some-component as |block|}} -> This is block {{block}} -> {{else as |block|}} -> This is block {{block}} -> {{/some-component}} -> ``` +The proposal conflicts with the current behavior of `else` inverse blocks. It sort of duplicates the limited functionality provided by the current `yield` helper when called with the `to` attribute for yielding to the inverse block. If implemented it could create some ambiguity around yielding to a block named "else", as illustrated below. + +
+ + Alternative to inverse blocks introduced by this proposal + +

+ > **some-component.hbs** + > ```hbs + > {{yield "default"}} + > {{yield:else "inverse"}} + > ``` + > + > **sample-invocation.hbs** + > ```hbs + > {{#some-component as |block|}} + > This is block {{block}} + > {{:else as |block|}} + > This is block {{block}} + > {{/some-component}} + > ``` +

+
+ +
+ + Compared to the current syntax + +

+ > **some-component.hbs** + > ```hbs + > {{yield "default"}} + > {{yield to="inverse" "inverse"}} + > ``` + > + > **sample-invocation.hbs** + > ```hbs + > {{#some-component as |block|}} + > This is block {{block}} + > {{else as |block|}} + > This is block {{block}} + > {{/some-component}} + > ``` +

+
# Alternatives -### Past RFCs +**Past RFCs** This proposal was inspired by and draws knowledge from [this unresolved RFC](https://github.com/emberjs/rfcs/pull/72) for named yields and [this closed RFC](https://github.com/emberjs/rfcs/pull/43) for multiple yields. Ultimately there was no consensus on a syntax that was both clear and easy to teach but also technically desirable. -### Implementation alternatives - -The main criticism of [the closed RFC](https://github.com/emberjs/rfcs/pull/43) was that the proposed syntax was dynamic and not statically analyzable. A dynamic version of this proposal would also be possible, looking something like this: - -> **some-component.hbs** -> ```hbs -> {{yield to="block-b" 'B'}} -> {{yield to="block-a" 'A'}} -> ``` -> -> **sample-invocation.hbs** -> ```hbs -> {{#some-component:block 'block-a' as |block|}} -> This is block {{block}} -> {{block 'block-b' as |block|}} -> This is block {{block}} -> {{/some-component}} -> ``` -> -> **Result** -> ``` -> This is block B -> This is block A -> ``` +**Implementation alternatives** + +The main criticism of [the closed RFC](https://github.com/emberjs/rfcs/pull/43) was that the proposed syntax was dynamic and not statically analyzable. A dynamic version of this proposal would also be possible, as illustrated below. + +
+ + Dynamic named block/yields implementation + +

+ > **some-component.hbs** + > ```hbs + > {{yield to="block-b" 'B'}} + > {{yield to="block-a" 'A'}} + > ``` + > + > **sample-invocation.hbs** + > ```hbs + > {{#some-component:block 'block-a' as |block|}} + > This is block {{block}} + > {{block 'block-b' as |block|}} + > This is block {{block}} + > {{/some-component}} + > ``` + > + > **Result** + > ``` + > This is block B + > This is block A + > ``` +

+
The dynamic alternative follows the current syntax and semantics around inverse blocks more closely, but since yielding to inverse blocks is not even very well documented, that may not be as salient as the benefits of static analyzability. @@ -201,42 +225,53 @@ Another alternative is to automagically yield some sort of hash of named blocks The primary alternative for this is to just forgo named blocks completely and rely on contextual components for composability. However, I feel that named blocks/yields solve problems with flexibility in content composability that contextual components just cannot. Another way to achieve [something like named yields](https://github.com/emberjs/rfcs/pull/72#issuecomment-219174876) wihtout actually implementing it was suggested by [@foxnewsnetwork](https://github.com/foxnewsnetwork). However, this simulation of named yields is arguably hard to teach/understand. -### Syntax alternatives +**Syntax alternatives** Some people (including me) are having doubts about the `:` delimiter syntax for named block and yield invocation. I was also considering `@` or `::`, as previewed below. -With `@`: -> **some-component-at.hbs** -> ```hbs -> {{yield@block-b 'B'}} -> {{yield@block-a 'A'}} -> ``` -> -> **sample-invocation-at-syntax.hbs** -> ```hbs -> {{#some-component-at@block-a as |block|}} -> This is block {{block}} -> {{@block-b as |block|}} -> This is block {{block}} -> {{/some-component-at}} -> ``` -> - -With `::`: -> **some-component-double.hbs** -> ```hbs -> {{yield::block-b 'B'}} -> {{yield::block-a 'A'}} -> ``` -> -> **sample-invocation-double-syntax.hbs** -> ```hbs -> {{#some-component-double::block-a as |block|}} -> This is block {{block}} -> {{::block-b as |block|}} -> This is block {{block}} -> {{/some-component-double}} -> ``` +
+ + With `@` + +

+ > **some-component-at.hbs** + > ```hbs + > {{yield@block-b 'B'}} + > {{yield@block-a 'A'}} + > ``` + > + > **sample-invocation-at-syntax.hbs** + > ```hbs + > {{#some-component-at@block-a as |block|}} + > This is block {{block}} + > {{@block-b as |block|}} + > This is block {{block}} + > {{/some-component-at}} + > ``` +

+
+ +
+ + With `::` + +

+ > **some-component-double.hbs** + > ```hbs + > {{yield::block-b 'B'}} + > {{yield::block-a 'A'}} + > ``` + > + > **sample-invocation-double-syntax.hbs** + > ```hbs + > {{#some-component-double::block-a as |block|}} + > This is block {{block}} + > {{::block-b as |block|}} + > This is block {{block}} + > {{/some-component-double}} + > ``` +

+
# Unresolved questions From ba6be90bfe355d65919d898211e6184387a4af3c Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 16:13:03 -0800 Subject: [PATCH 14/16] Fix paragraph formatting Fix paragraph formatting within "Why ever?" section --- text/0000-named-blocks-and-yields.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index 604fb86deb..a98bf71d6a 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -16,18 +16,25 @@ Glimmer is stable and Ember 2.x has been around for a while. It's about time to **Why ever?** +

Components in Ember are meant to abstract away meaningfully atomic chunks of UI behavior, e.g. a drop down menu element (cf. [ember-power-select](https://github.com/cibernox/ember-power-select)). Currently, the interface to the component abstraction consists of: - - * any number of attributes (and optionally many positional params) - * a single passed block (and optionally a single passed inverse block) - +

+ +
    +
  • any number of attributes (and optionally many positional params)
  • +
  • a single passed block (and optionally a single passed inverse block)
  • +
+ +

You will note that you can pass many attributes or params through a component's interface, but only a single block. I think this has led to a trend in how we design high-level, complex components which have to be flexible to fit many use cases. We've started building components that are more configurable than they are composable. - +

+

The goal of both configurability and composability is to provide flexibility in how you go from input to output over a collection of interfaces in a system. Gaining this desired flexibility via configurability tends to lead to fewer interfaces but more parameters to those interfaces. Composability usually means many, more atomic interfaces that fit together with less parameters. Take the example of JS functions: you can perform complex logic on input by calling a single functions that takes in the input plus a giant config object telling you how to handle the input, or you can break the logic into multiple functions that call each other. Logic written in a single function with many configuration parameters will always be less flexible, maintainable, and reusable than logic contained in many atomic functions. - +

+

In Ember components, we achieve composability by passing blocks that call other components. Configurability, on the other hand, is achieved by passing attributes and positional parameters. Flexibility, in high-level Ember UI components, is determined by the variability of rendered content that can be achieved by a single component. Limiting block passing to a single, un-named block means we can only *wrap* the passed block in content, rather than arbitrarily compose content with multiple passed blocks of content. This leads to things like conditionally rendering some content based on passed attributes/parameters or conditionally yielding different block parameters. Yielding to multiple named blocks would make the use of a lot of the configuration that's currently happening unnecessary and, in turn, encourage composition of components instead. -

+

# Detailed design From 459ec4027fac8b5a04e935e977edb7e6402c2326 Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 17:34:33 -0800 Subject: [PATCH 15/16] Format consistency Use details on "Why now" subsection --- text/0000-named-blocks-and-yields.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index a98bf71d6a..34227dbf98 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -8,9 +8,15 @@ Allow yielding to multiple named blocks in an Ember component. # Motivation -**Why now?** - -Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea since it was always well-liked but just happened to never come up at the right time. +
+ + **Why now?** + +

+

+ Glimmer is stable and Ember 2.x has been around for a while. It's about time to get back to this idea since it was always well-liked but just happened to never come up at the right time. +

+
From d98299965f75c648accd9eea061bda912f5e76af Mon Sep 17 00:00:00 2001 From: Spencer Claxton Date: Mon, 19 Dec 2016 21:00:46 -0800 Subject: [PATCH 16/16] Add more abstract syntax examples --- text/0000-named-blocks-and-yields.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/text/0000-named-blocks-and-yields.md b/text/0000-named-blocks-and-yields.md index 34227dbf98..9f52380d40 100644 --- a/text/0000-named-blocks-and-yields.md +++ b/text/0000-named-blocks-and-yields.md @@ -47,6 +47,19 @@ Allow yielding to multiple named blocks in an Ember component. To achieve yielding to more than one named block I propose adding the following built-in syntax and semantics to components: +``` +{{yield: ...blockParams}} +``` +``` +{{# as |...blockParams|}} + ...stuff +{{: as |...blockParams|}} + ...other stuff +{{/}} +``` + +The following is a concrete example of this: + > **some-component.hbs** > ```hbs > {{yield:block-b 'B'}} @@ -75,6 +88,16 @@ To achieve yielding to more than one named block I propose adding the following If the default block is omitted, to ease developer ergonomics I propose the following additional syntax: +``` +{{#: as |...blockParams|}} + ...stuff +{{:}} + ...other stuff +{{/}} +``` + +The following is a concrete example of this: + > **some-component.hbs** > ```hbs > {{yield:block-b 'B'}} @@ -96,7 +119,7 @@ additional syntax: > This is block A > ``` -We would also need to include a corresponding continuation of the `hasBlock` property syntax currently available in component templates (credit goes to [@bendemboski](https://github.com/bendemboski) for this addition): +We would also need to include a corresponding continuation of the `hasBlock` property syntax currently available in component templates, namely just `hasBlock:` (credit goes to [@bendemboski](https://github.com/bendemboski) for this addition): > **some-component.hbs** > ```hbs