14

I'm trying to use the styleUrls property when declaring my Angular 2 component to take advantage of View Encapsulation but there seems to be a problem when elements get inserted into the DOM after Angular has its completed initialization.

My situation is with a PrimeNG paginator that I currently can't style since it doesn't get applied a scope by Angular.

See below the <p-datatable> element has a scope (it existed in the original markup) but <p-paginator> does not (was added to DOM after the fact).

enter image description here

Therefore the style inserted by Angular into HEAD does not match my elements:

<style>
    p-datatable[_ngcontent-xnm-4] p-paginator[_ngcontent-xnm-4]:not(:first-child) {
    display: none;
}
</style>

Is this a limitation of view encapsulation in Angular 2 or is there a way to have it "re-scope" the DOM on demand?

edited for typo and clarified title

BlazingFrog
  • 2,265
  • 3
  • 21
  • 31
  • I was facing [the same issue with ng-bootstrap](https://github.com/ng-bootstrap/ng-bootstrap/issues/984), and the Angular UI team proposed the same solution, using ``:host \deep\``. So I guess Angular does not provide a way to scope dynamically added elements. – Arjan Oct 31 '16 at 15:27

1 Answers1

41

As you noticed this is because of the Shadow DOM and the style scoping it provides. Your template only contains the the p-datatable which correctly gets scoped, but its child elements that are added after the fact are NOT scoped. In order to apply your custom styling you can choose two approaches.

Solution 1 - Special Selectors (Recommended)

I personally recommend this as you can still maintain view encapsulation (Shadow DOM). We can still target our component level templates that use PrimeNG by using the :host and /deep/ selectors as such

:host /deep/ .ui-paginator-bottom {
  display: none;
}

What this does is force a style down through the child component tree into all the child component views so even though the p-datatable is the only tag present in your component's own template, the style will still be applied as it is a child to the component in the DOM.

Solution 2 - Disable View Encapsulation

At the component level you can disable the View Encapsulation by setting it to ViewEncapsulation.None as such

...
import { ..., ViewEncapsulation } from '@angular/core';

@Component {
...
encapsulation: ViewEncapsulation.None,
}
AhmedBM
  • 1,202
  • 17
  • 15
  • 2
    If only I could upvote it 10 times. This answer is perfect and I couldn't find this info anywhere in their documentation or the forum. Thank you! – reflog Nov 21 '16 at 09:18
  • 1
    I 100% agree with reflog. Was hunting for this solution for the past 3 hours. Thanks! – Tony Scialo Mar 01 '17 at 19:37
  • Great answer! This helped me big times! – mXX Mar 21 '17 at 09:49
  • Wow, I literally have never seen either the ":host" or "/deep" deep selectors...ever. – cjones26 Sep 20 '17 at 17:31
  • Just for future reference: note that `encapsulation: ViewEncapsulation.None` implies that any scoping for `@import` is ignored too. So, even scoping with some `.non-existing` in `.non-existing { @import '../../node_modules/bootstrap/scss/bootstrap'; }` has no effect; the imported CSS simply applies to the full page. (Likewise, some `.existing { @import '...'; }` might seem to work, but actually ignores the `.existing` too.) In other words, when using Angular CLI 6, one might just add the styles to `angular.json` instead. – Arjan May 29 '18 at 18:18
  • The solution with special selectors is exactly what I was looking for but on the official documentation it's indicated as deprecated: `The shadow-piercing descendant combinator is deprecated`. Will we only have the second solution in the future or are there other solutions replacing the first one ? – Chavjoh Jul 28 '21 at 11:24
  • If somebody tries to do the same with angular 13/14(probably some previous versions too), `/deep/` won't work, you have to use their `::ng-deep` instead – J4N Aug 05 '22 at 09:14