From 5667872d8cb7df50b48df7fd05fdb77f43f4e024 Mon Sep 17 00:00:00 2001 From: cibernox Date: Fri, 22 Dec 2017 13:42:21 +0100 Subject: [PATCH 1/8] Promote {{-in-element}} to public API --- text/0000-promote-in-element-to-public-api.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 text/0000-promote-in-element-to-public-api.md diff --git a/text/0000-promote-in-element-to-public-api.md b/text/0000-promote-in-element-to-public-api.md new file mode 100644 index 0000000000..60d1290c46 --- /dev/null +++ b/text/0000-promote-in-element-to-public-api.md @@ -0,0 +1,53 @@ +- Start Date: 2017-12-22 +- RFC PR: (leave this empty) +- Ember Issue: (leave this empty) + +# Summary + +Promote the private API `{{-in-element}}` to public API as `{{in-element}}`. + +# Motivation + +Developers need to render content out of the regular HTML flow, and `{{-in-element}}` for +that purpose, but it remains private (or _intimate_) API. People is usually wary of using +private APIs (and for good reason) as they may get removed at any time. + +If the core team and the community is happy with the current behavior of `{{-in-element}}` it's +time to make it public. + +# Detailed design + +Create a new `{{in-element}}` as simple alias of `{{-in-element}}` with the exact same signature +and semantics and deprecate the private one. +The deprecated private API will continue to exist until the first LTS release of the 3.X cycle (3.4) +to be finally removed in the next one (3.5). + +# How We Teach This + +This will be a new build-in helper and must be added to the guides and the API. +For most usages, it will replace some community solution created with the same goal, like +[ember-wormhole](https://github.com/yapplabs/ember-wormhole) or [ember-elsewhere](https://github.com/ef4/ember-elsewhere). +It would be for the best to let the authors of those addons know about this feature so they can +deprecate their packages if they feel there is no longer a need for them, or at least update their +Readme files to let their users know that there is a built-in solution in Ember that might cover +their needs. + +# Drawbacks + +By augmenting the public API of the framework, the framework is committing to support it for the lifespan +of an entire mayor version (Ember 4.0). + +# Alternatives + +We can decide that the framework does not want to make public and support this feature, and continue +to rely on community-built addons like we've done until today. + +# Unresolved questions + +Do we want to make any improvement to `{{-in-element}}` before making it public API? + +Some possible ideas: +- Allow to _conditionally_ render the block in place. See https://github.com/DockYard/ember-maybe-in-element +- Allow to receive not only DOM elements as first argument, but also strings, representing the ID of + other CSS selector. +- Modify or improve the way it behaves during SSR using ember-fastboot. \ No newline at end of file From 1208d567761a222e2a64554f066ef123fd73a370 Mon Sep 17 00:00:00 2001 From: cibernox Date: Fri, 22 Dec 2017 13:50:14 +0100 Subject: [PATCH 2/8] Improve comment about deprecation --- text/0000-promote-in-element-to-public-api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-promote-in-element-to-public-api.md b/text/0000-promote-in-element-to-public-api.md index 60d1290c46..f55ed3d630 100644 --- a/text/0000-promote-in-element-to-public-api.md +++ b/text/0000-promote-in-element-to-public-api.md @@ -19,7 +19,8 @@ time to make it public. Create a new `{{in-element}}` as simple alias of `{{-in-element}}` with the exact same signature and semantics and deprecate the private one. -The deprecated private API will continue to exist until the first LTS release of the 3.X cycle (3.4) +Although the API is technically private, there I believe there is enough people using it to deserve +a deprecation. The deprecated private API will continue to exist until the first LTS release of the 3.X cycle (3.4) to be finally removed in the next one (3.5). # How We Teach This From 8b8e9a7db237bc984675d76d8d46a1f85448aa7f Mon Sep 17 00:00:00 2001 From: cibernox Date: Sat, 23 Dec 2017 23:30:30 +0100 Subject: [PATCH 3/8] Apply suggestions --- text/0000-promote-in-element-to-public-api.md | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/text/0000-promote-in-element-to-public-api.md b/text/0000-promote-in-element-to-public-api.md index f55ed3d630..b308efdf1c 100644 --- a/text/0000-promote-in-element-to-public-api.md +++ b/text/0000-promote-in-element-to-public-api.md @@ -8,20 +8,38 @@ Promote the private API `{{-in-element}}` to public API as `{{in-element}}`. # Motivation -Developers need to render content out of the regular HTML flow, and `{{-in-element}}` for -that purpose, but it remains private (or _intimate_) API. People is usually wary of using -private APIs (and for good reason) as they may get removed at any time. +Sometimes developers need to render content out of the regular HTML flow. This concept is often also +called "portals". Some components like dropdowns and modals use this technique to render stuff close +to the root of the page to bypass CSS overflow rules. Some apps that are embedded into static pages +even use this technique to update parts of the page **outside** the app itself. + +This need need has been covered by solutions developed in the user space but it was so common that +glimmer baked it into the VM in the form of `{{-in-element}}`, but it remains private (or _intimate_) API. +People is usually wary of using private APIs (and for good reason) as they may get removed at any time. If the core team and the community is happy with the current behavior of `{{-in-element}}` it's time to make it public. # Detailed design -Create a new `{{in-element}}` as simple alias of `{{-in-element}}` with the exact same signature -and semantics and deprecate the private one. -Although the API is technically private, there I believe there is enough people using it to deserve -a deprecation. The deprecated private API will continue to exist until the first LTS release of the 3.X cycle (3.4) -to be finally removed in the next one (3.5). +The existing API of `{{-in-element}}` is very simple and I do not suggest making any change to it. +It takes a DOM element as the only positional param and a block, and renders that block _inside_ the +given element instead of where it would normally go. + +P.e. + +```hbs +{{#-in-element destinationElement}} +
Some content
+{{/-in-element}} +``` + +The current implementation only suggests creating a new `{{in-element}}` construct that is a simple +alias of `{{-in-element}}` with the exact same params and behavior, and then, after a while, remove +the private one. +Although `{{-in-element}}` is technically private, there there is enough people using it to deserve +a deprecation. I suggest keeping the deprecated private API will until the first LTS release of the +3.X cycle (3.4) to be finally removed in the next one (3.5). # How We Teach This From 64946b7c3d49325fbe80b15a0adbee8713d64a19 Mon Sep 17 00:00:00 2001 From: cibernox Date: Sun, 24 Dec 2017 00:24:00 +0100 Subject: [PATCH 4/8] Update notes about behavior --- text/0000-promote-in-element-to-public-api.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/text/0000-promote-in-element-to-public-api.md b/text/0000-promote-in-element-to-public-api.md index b308efdf1c..ea0315dfe3 100644 --- a/text/0000-promote-in-element-to-public-api.md +++ b/text/0000-promote-in-element-to-public-api.md @@ -23,10 +23,13 @@ time to make it public. # Detailed design The existing API of `{{-in-element}}` is very simple and I do not suggest making any change to it. -It takes a DOM element as the only positional param and a block, and renders that block _inside_ the -given element instead of where it would normally go. -P.e. +* It takes a single positional param `destinationElement` that is a DOM element, and a block +* The given block is rendered not where it is located, but inside the given `destination` element. +* If `destinationElement` is false/null/undefined then it doesn't render anything but it doesn't error. +* If `destinationElement` changes the block is removed from the previous destination and added to the new one. + +Example usage: ```hbs {{#-in-element destinationElement}} @@ -37,6 +40,7 @@ P.e. The current implementation only suggests creating a new `{{in-element}}` construct that is a simple alias of `{{-in-element}}` with the exact same params and behavior, and then, after a while, remove the private one. + Although `{{-in-element}}` is technically private, there there is enough people using it to deserve a deprecation. I suggest keeping the deprecated private API will until the first LTS release of the 3.X cycle (3.4) to be finally removed in the next one (3.5). From e39be19fea3d039e0663a527b77420dea6f440f2 Mon Sep 17 00:00:00 2001 From: cibernox Date: Wed, 27 Dec 2017 10:29:32 +0100 Subject: [PATCH 5/8] Save clarifications --- text/0000-promote-in-element-to-public-api.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/text/0000-promote-in-element-to-public-api.md b/text/0000-promote-in-element-to-public-api.md index ea0315dfe3..3551ef2b63 100644 --- a/text/0000-promote-in-element-to-public-api.md +++ b/text/0000-promote-in-element-to-public-api.md @@ -25,9 +25,17 @@ time to make it public. The existing API of `{{-in-element}}` is very simple and I do not suggest making any change to it. * It takes a single positional param `destinationElement` that is a DOM element, and a block -* The given block is rendered not where it is located, but inside the given `destination` element. +* The given block is rendered not where it is located, but inside the given `destination` element, at +the end of it if there is any other content on the destination element. * If `destinationElement` is false/null/undefined then it doesn't render anything but it doesn't error. -* If `destinationElement` changes the block is removed from the previous destination and added to the new one. +* If `destinationElement` changes the block is removed from the previous destination and added to the new one. This +process tears down the rendered content on the initial destination and renders it again on the new one, meaning +that any component withing the block will be destroyed and instantiated again, so transient HTML state +like the value of an input will be lost unless manually preserved somewhere else, like a service. +* If the destination element is an invalid value (a string, a number ...) it throws an `parent.insertBefore is not a function` error. I think +that throwing an error is correct but the error message could be improved. +* If the destination element has a different context than the origin (SVG) the content respects the +context of the parent. Example usage: From 25a013d3b3cfb08282996530ff3cc2b96976813a Mon Sep 17 00:00:00 2001 From: cibernox Date: Wed, 3 Jan 2018 00:11:45 +0100 Subject: [PATCH 6/8] Update RFC --- text/0000-promote-in-element-to-public-api.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/text/0000-promote-in-element-to-public-api.md b/text/0000-promote-in-element-to-public-api.md index 3551ef2b63..1e542362fb 100644 --- a/text/0000-promote-in-element-to-public-api.md +++ b/text/0000-promote-in-element-to-public-api.md @@ -24,18 +24,23 @@ time to make it public. The existing API of `{{-in-element}}` is very simple and I do not suggest making any change to it. -* It takes a single positional param `destinationElement` that is a DOM element, and a block +* It takes a single positional param `destinationElement` that is a DOM element, and a block. * The given block is rendered not where it is located, but inside the given `destination` element, at the end of it if there is any other content on the destination element. -* If `destinationElement` is false/null/undefined then it doesn't render anything but it doesn't error. +* If `destinationElement` is null/undefined then it doesn't render anything but it doesn't error. +* If `destinationElement` is false/0/"" it raises an assertion in development but fails silently in production. * If `destinationElement` changes the block is removed from the previous destination and added to the new one. This process tears down the rendered content on the initial destination and renders it again on the new one, meaning -that any component withing the block will be destroyed and instantiated again, so transient HTML state -like the value of an input will be lost unless manually preserved somewhere else, like a service. +that any component withing the block will be destroyed and instantiated again (calling the appropiate lifecycle hooks), +so transient HTML state like the value of an input will be lost unless manually preserved somewhere else, like a service. * If the destination element is an invalid value (a string, a number ...) it throws an `parent.insertBefore is not a function` error. I think that throwing an error is correct but the error message could be improved. -* If the destination element has a different context than the origin (SVG) the content respects the -context of the parent. +* If the destination element has a different context (like SVG) the content will be appended normally by the glimmer VM, +which doesn't try to validate the correctness of the generated HTML. This is normal behavior in Glimmer, not +an exception, and users must be aware that rendering invalid markup might be interpreted or auto-corrected in +unexpected ways by the browser when in SSR mode. +* Rendering into a foreign object (an element within an `