You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ViewEncapsulation not working on nested components with ng-content #7400
AFAIK the style compiler flags all element selectors with the current component variable to ensure that no outside element is affected by the component's style. The problem here is, that ngContent is using your HTML as is and no additional attribute would be added to those elements (to match to the generated CSS).
But as you are already somehow "scoping" your CSS by yourself you could switch your component to view encapsulation mode "None" (encapsulation: ViewEncapsulation.None) to skip the shimming of the CSS.
With this selector :host::content a { the link becomes green. I expected ::content alone to work but AFAIR there is the same issue in shimmed shadow DOM in Polymer.
It also works with encapsulation: ViewEncapsulation.None
Ah, nice! This is a good catch with ":host::content" but ":host" is enough. This scopes the "a" and the shim does not trigger for it, as it is already scoped.
I also tried the :host::content syntax suggested by @zoechi , but as you can see from the Plunker, it doesn't change a thing.
This is a new regression, since our code worked perfectly regarding this in RC.4, but no more in RC.6 (finished the RC.4 -> RC.6 update last Friday).
I can overcome the situation using ViewEncapsulation.None, but I really, really do not want to do that, since that would also affect siblings and parents, which is AFAIK the specific reason why to use some view encapsulation in the first place.
Okay, as tested, this original issue #7400 was invalid when it was created (3rd March), but now the issue is real for RC.6 and RC.7, probably also for RC.5, but I haven't tested it. In RC.4 and older this worked., but is not working anymore.
Should I open a new bug report on this (since this is a new regression / new feature change), or is anyone anywhere inside Angular2 team going to even see this comment here, and acknowledge the possibly nasty bug (or even nastier feature change...)?
@vicb Thanks for your quick response, and sorry to hear that.
While this might be the correct way according to shadow DOM emulation, this limits the usefulness of the (and ), since direct children for component works now differently than indirect children.
To overcome the issue, you can:
A) Set parent component encapsulation to ViewEncapsulation.None
B) Use LESS or similar (otherwise will be a major PITA) for generating CSS
C) replace ":host { /* all your styles here for component and children / }"
with "parent-component-tag-name { / all your styles here for component and children */ }"
With above three-step process, the component styling works pretty much similar to what it was for all Angular2 versions from alpha & beta to RC.4,
Caveat:
You better not forget to prefix your styles with "parent-component-tag-name", and you better not use the same component selector twice for different components.
Probably you could think about a more convenient way to style components (or native HTML elements!) inside the , without totally breaking the encapsulation.
I just educated myself about Shadow DOM and it seems that we probably should have a "deep" combinator (previously "/deep/" in v0, currently ">>>" in v1). That would be a direct solution for my problem.
I'll try the >>> selector, and if it's not working with ViewEncapsulation.Emulated, I'll open a new bug report for Angular2.
that being said >>> is not supposed to style the projected content in the native implementation.
it will for in emulated mode because of a limitation that we might fix at some point.
so best is to style your projected content in its parent component as you would do with the native impl.
You can check "shadow DOM 101, ..." articles - they are about v0 but concepts are still valid.
And if there are fellow LESS users, seems that LESS compiler dislikes >>> syntax, so you need to add an alias for that, eg. '@deep: ~">>>"; '
and then use that like '@{deep} { /* your deep styles here */ }
@vicb
"that being said >>> is not supposed to style the projected content in the native implementation."
Now you lost me completely. According to all sources I can find, the "Shadow-Piercing descentant combinator" should do exactly that for Shadow DOM, which is, piercing the shadow for all descendants.
If you don't need to pierce shadow DOM, then you simply don't need the >>> in the first place. But it's not a bug or limitation, that shadow piercing descendant combinator pierces the shadow for descendants. And I will definitely rely on this feature.
The /deep/ combinator is similar to ::shadow, but more powerful. It completely ignores all shadow boundaries and crosses into any number of shadow trees. Put simply, /deep/ allows you to drill into an element's guts and target any node.
The /deep/ combinator is particularly useful in the world of Custom Elements where it's common to have multiple levels of Shadow DOM. Prime examples are nesting a bunch of custom elements (each hosting their own shadow tree) or creating an element that inherits from another using ."
Simply put: "Put simply, /deep/ allows you to drill into an element's guts and target any node."
I tried to do several google searches about the subject, but I found nothing about shadow-piercing not affecting projected content. I think that's simply because shadow-piercing "completely ignores all shadow boundaries and crosses into any number of shadow trees"
But alas, the battle is lost already. Google Chrome's team has failed us, and proposed that the shadow-piercing was a mistake, and should be discarded anyway. And actually their proposal won, so shadow-piercing is now officially deprecated, and will be removed at some point. Sad, but true.
So the "correct" way to overcome this sad situation is to just ditch the shadow dom completely for components, that are meant for containers, such as custom "table" component. With shadow-piercing deprecated, you can no more style eg. "tr" and "td" elements in your custom table component in a sane way, instead of completely ditching the view encapsulation, and thus not using HTML-compatible native components in the future.
There might be good performance / security / whatever related reasons why Google and others are ditching the shadow-piercing, but the cost in the developer end is also high. Sure, you can then introduce eg. "tr-that-should-be-used-in-that-particular-custom-table-component" -component, and "td-that-should-be-used-in-that-particular-custom-tr-for-that-particular-custom-table-component", but really, what a huge pain. And what about having a 2-tone background colors for every second "custom-tr" in that particular "custom-table"?
... or something similar. That would get us much further, without piercing the shadow. But I guess the limits of current "ng-content" are derived from the limitations of native components "content".
@vicb
I opened a new issue (#11595) about supporting ::slotted() (old name was ::content) selector, which would help over the issue without completely ditching the view encapsulation. In the meantime, we will use ViewEncapsulation.None for those container components.
Thanks for your good work on Angular2, I appreciate it a lot!
@rvalimaki the shadow piercing combinators were deprecated because the mere existence prevents some performance optimization strategies that shadow DOM boundaries allow.
The proposed replacement are CSS variables (not yet supported by Angular ViewEncapsulation.Emulated) and hopefully CSS mixins as Polymer provides them as shims.
The >>> and /deep/ are mostly a workaround for the missing CSS variables shims (CSS variables alone are too weak though anyway).
Thanks, looking forward for get those various Shadow DOM styling features implemented in Angular2, including ::slotted(). But already the ::slotted() is only a limited version of ::content, allowing reference only to top level elements of "slot" (or "ng-content" in Angular2 vocabulary), instead that all elements as ::content was able to do. I guess that performance might be the reason for ::slotted() being very limited on focus.
In addition to #11595 issue I opened about ::slotted(), there is already issues #10663 and #2567 open about CSS variable shimming and style hooks.
Otherwise this issue #7400 is closed as it works as advertised, which is sad, but beyond ng2 team to fix. It's the way web components are currently supposed to work, even if it feels a bit brain dead at the first sight, to be honest. I do believe that they had good (performance related) reasons for that decision in W3C though, so this will never be "fixed".
When #11595 is fixed (not holding my breath...) to match current web components standard, that'll help on simple situation like in your plunker, though. That is, addressing to direct children inside <ng-content> should be possible using ::slotted(), though it's not yet implemented. You cannot address other ancestors that way though.
<ng-content>
<div>You should be able to address this div using ::slotted() syntax
<div>...but this nested div you cannot access with ::slotted(), sorry!</div>
</div>
</ng-content>
Reacted by Neo Minchul Chae
angular-automatic-lock-bot commented on Sep 11, 2019
Activity
thomashilzendegen commentedon Mar 3, 2016
AFAIK the style compiler flags all element selectors with the current component variable to ensure that no outside element is affected by the component's style. The problem here is, that ngContent is using your HTML as is and no additional attribute would be added to those elements (to match to the generated CSS).
But as you are already somehow "scoping" your CSS by yourself you could switch your component to view encapsulation mode "None" (encapsulation: ViewEncapsulation.None) to skip the shimming of the CSS.
zoechi commentedon Mar 3, 2016
With this selector
:host::content a {the link becomes green. I expected::contentalone to work but AFAIR there is the same issue in shimmed shadow DOM in Polymer.It also works with
encapsulation: ViewEncapsulation.Nonethomashilzendegen commentedon Mar 3, 2016
Ah, nice! This is a good catch with ":host::content" but ":host" is enough. This scopes the "a" and the shim does not trigger for it, as it is already scoped.
rvalimaki commentedon Sep 12, 2016
I'm quite sure this was already working on ie. RC.4 (and also in all older versions we did actually use), but broken in RC.6.
I did make a quick Plunker about the situation and my problem:
https://plnkr.co/edit/R3agaCYIVWK6UnscMEwA?p=preview
I also tried the :host::content syntax suggested by @zoechi , but as you can see from the Plunker, it doesn't change a thing.
This is a new regression, since our code worked perfectly regarding this in RC.4, but no more in RC.6 (finished the RC.4 -> RC.6 update last Friday).
I can overcome the situation using ViewEncapsulation.None, but I really, really do not want to do that, since that would also affect siblings and parents, which is AFAIK the specific reason why to use some view encapsulation in the first place.
rvalimaki commentedon Sep 13, 2016
Okay, as tested, this original issue #7400 was invalid when it was created (3rd March), but now the issue is real for RC.6 and RC.7, probably also for RC.5, but I haven't tested it. In RC.4 and older this worked., but is not working anymore.
This is a modified plunker from the original post, with ":host" added, and as you can see, it did work perfectly (beta.7):
http://plnkr.co/edit/UNSjGDYB04S3lpWScKgf?p=preview
I can also assure you that it did work for RC.4 as well.
However, as you can see from my recent plunker (RC.6), this is not working anymore:
https://plnkr.co/edit/R3agaCYIVWK6UnscMEwA?p=preview
Should I open a new bug report on this (since this is a new regression / new feature change), or is anyone anywhere inside Angular2 team going to even see this comment here, and acknowledge the possibly nasty bug (or even nastier feature change...)?
vicb commentedon Sep 13, 2016
Saw it. If think this is the correct behavior. Let me double check and explain when I'm at work in a bit
vicb commentedon Sep 13, 2016
With shadow DOM projected content keep the style at their original location.
Works as expected.
rvalimaki commentedon Sep 14, 2016
@vicb Thanks for your quick response, and sorry to hear that.
While this might be the correct way according to shadow DOM emulation, this limits the usefulness of the (and ), since direct children for component works now differently than indirect children.
To overcome the issue, you can:
A) Set parent component encapsulation to ViewEncapsulation.None
B) Use LESS or similar (otherwise will be a major PITA) for generating CSS
C) replace ":host { /* all your styles here for component and children / }"
with "parent-component-tag-name { / all your styles here for component and children */ }"
With above three-step process, the component styling works pretty much similar to what it was for all Angular2 versions from alpha & beta to RC.4,
Caveat:
You better not forget to prefix your styles with "parent-component-tag-name", and you better not use the same component selector twice for different components.
Probably you could think about a more convenient way to style components (or native HTML elements!) inside the , without totally breaking the encapsulation.
rvalimaki commentedon Sep 14, 2016
@vicb
I just educated myself about Shadow DOM and it seems that we probably should have a "deep" combinator (previously "/deep/" in v0, currently ">>>" in v1). That would be a direct solution for my problem.
I'll try the >>> selector, and if it's not working with ViewEncapsulation.Emulated, I'll open a new bug report for Angular2.
https://drafts.csswg.org/css-scoping-1/#deep-combinator
zoechi commentedon Sep 14, 2016
In shadow DOM the
::contentselector was used to address transcluded content.What I figured out to work is
::contentin combination with>>>https://plnkr.co/edit/fhaOzBmIVKkxVGshOzp4?p=preview
>>>shouldn't be necessary here.See also http://stackoverflow.com/questions/37763252/custom-styling-on-ng-content-in-angular2-not-working/37764548#37764548
vicb commentedon Sep 14, 2016
@rvalimaki both
>>>and/deep/work to reach nested components.@zoechi
::contentis ignored by the shim.vicb commentedon Sep 14, 2016
that being said
>>>is not supposed to style the projected content in the native implementation.it will for in emulated mode because of a limitation that we might fix at some point.
so best is to style your projected content in its parent component as you would do with the native impl.
You can check "shadow DOM 101, ..." articles - they are about v0 but concepts are still valid.
rvalimaki commentedon Sep 14, 2016
@zoechi, @vicb
Thanks! That's perfect.
Instead of ::content, we should use :host. Otherwise encapsulation doesn't work.
so the correct syntax is:
styles: [
:host >>> h1 { background: yellow !important; font-size: 10px !important; }],And if there are fellow LESS users, seems that LESS compiler dislikes >>> syntax, so you need to add an alias for that, eg. '@deep: ~">>>"; '
and then use that like '@{deep} { /* your deep styles here */ }
zoechi commentedon Sep 14, 2016
@vicb I see. Thanks for the update.
rvalimaki commentedon Sep 14, 2016
@vicb
"that being said >>> is not supposed to style the projected content in the native implementation."
Now you lost me completely. According to all sources I can find, the "Shadow-Piercing descentant combinator" should do exactly that for Shadow DOM, which is, piercing the shadow for all descendants.
If you don't need to pierce shadow DOM, then you simply don't need the >>> in the first place. But it's not a bug or limitation, that shadow piercing descendant combinator pierces the shadow for descendants. And I will definitely rely on this feature.
vicb commentedon Sep 14, 2016
"Shadow-Piercing" pierces ... (suspens) ... the shadow, not the projected content.
read the 101/201.
rvalimaki commentedon Sep 14, 2016
@vicb:
For example: http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/
"The /deep/ combinator
The /deep/ combinator is similar to ::shadow, but more powerful. It completely ignores all shadow boundaries and crosses into any number of shadow trees. Put simply, /deep/ allows you to drill into an element's guts and target any node.
The /deep/ combinator is particularly useful in the world of Custom Elements where it's common to have multiple levels of Shadow DOM. Prime examples are nesting a bunch of custom elements (each hosting their own shadow tree) or creating an element that inherits from another using ."
Simply put: "Put simply, /deep/ allows you to drill into an element's guts and target any node."
I tried to do several google searches about the subject, but I found nothing about shadow-piercing not affecting projected content. I think that's simply because shadow-piercing "completely ignores all shadow boundaries and crosses into any number of shadow trees"
But alas, the battle is lost already. Google Chrome's team has failed us, and proposed that the shadow-piercing was a mistake, and should be discarded anyway. And actually their proposal won, so shadow-piercing is now officially deprecated, and will be removed at some point. Sad, but true.
So the "correct" way to overcome this sad situation is to just ditch the shadow dom completely for components, that are meant for containers, such as custom "table" component. With shadow-piercing deprecated, you can no more style eg. "tr" and "td" elements in your custom table component in a sane way, instead of completely ditching the view encapsulation, and thus not using HTML-compatible native components in the future.
There might be good performance / security / whatever related reasons why Google and others are ditching the shadow-piercing, but the cost in the developer end is also high. Sure, you can then introduce eg. "tr-that-should-be-used-in-that-particular-custom-table-component" -component, and "td-that-should-be-used-in-that-particular-custom-tr-for-that-particular-custom-table-component", but really, what a huge pain. And what about having a 2-tone background colors for every second "custom-tr" in that particular "custom-table"?
But anyway, this is not Angular2 related anymore.
rvalimaki commentedon Sep 14, 2016
"But anyway, this is not Angular2 related anymore."
Though in a way, that could be Angular2 related. If we only did get a really clever selectors that could allow you to do following:
And then in custom-container template:
... or something similar. That would get us much further, without piercing the shadow. But I guess the limits of current "ng-content" are derived from the limitations of native components "content".
rvalimaki commentedon Sep 14, 2016
@vicb
I opened a new issue (#11595) about supporting ::slotted() (old name was ::content) selector, which would help over the issue without completely ditching the view encapsulation. In the meantime, we will use ViewEncapsulation.None for those container components.
Thanks for your good work on Angular2, I appreciate it a lot!
zoechi commentedon Sep 14, 2016
@rvalimaki the shadow piercing combinators were deprecated because the mere existence prevents some performance optimization strategies that shadow DOM boundaries allow.
The proposed replacement are CSS variables (not yet supported by Angular
ViewEncapsulation.Emulated) and hopefully CSS mixins as Polymer provides them as shims.The
>>>and/deep/are mostly a workaround for the missing CSS variables shims (CSS variables alone are too weak though anyway).rvalimaki commentedon Sep 14, 2016
@zoechi
Thanks, looking forward for get those various Shadow DOM styling features implemented in Angular2, including ::slotted(). But already the ::slotted() is only a limited version of ::content, allowing reference only to top level elements of "slot" (or "ng-content" in Angular2 vocabulary), instead that all elements as ::content was able to do. I guess that performance might be the reason for ::slotted() being very limited on focus.
In addition to #11595 issue I opened about ::slotted(), there is already issues #10663 and #2567 open about CSS variable shimming and style hooks.
mcchae7 commentedon Apr 19, 2017
I have the same issue in Angular 4.0.2.
Do you know how can I fix this?
Reproduce: http://plnkr.co/edit/wg6xSs0wsulVg8n1QzcB?p=preview
rvalimaki commentedon Apr 19, 2017
@mcchae7
Until bug / missing feature #11595 is fixed, the only way to fix this is using ViewEncapsulation.None. Please see modified plunker (listitem.ts):
http://plnkr.co/edit/yO9SwBrBaUjY1SoTwFaf?p=preview
Otherwise this issue #7400 is closed as it works as advertised, which is sad, but beyond ng2 team to fix. It's the way web components are currently supposed to work, even if it feels a bit brain dead at the first sight, to be honest. I do believe that they had good (performance related) reasons for that decision in W3C though, so this will never be "fixed".
When #11595 is fixed (not holding my breath...) to match current web components standard, that'll help on simple situation like in your plunker, though. That is, addressing to direct children inside
<ng-content>should be possible using::slotted(), though it's not yet implemented. You cannot address other ancestors that way though.angular-automatic-lock-bot commentedon Sep 11, 2019
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.