Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Dan Wilson
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Dan Wilson@DanWilson )

Applying CSS Styles To Routable Child Views Using Simulated Encapsulation In Angular 4.4.4

By Ben Nadel on

The other day, I mentioned that routable view components, in Angular 4.4.4, don't need to have a defined "selector" property in their @Component() meta-data; but, I did say that including a selector could be helpful for debugging and / or styling the injected view. That said, apply CSS styles to an injected view isn't exactly straightforward when using simulated encapsulation. Since an injected view doesn't receive the simulated encapsulation "content" attribute like its surrounding elements do, we have to get a little clever if we want to safely target the injected view with CSS selectors.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

When using simulated encapsulation (the default mode) in an Angular component, the component element is given an "nghost" attribute. Then, all of the descendant elements, contained within the host element, are given matching "ngcontent" attributes. Therefore, if you view the rendered source of a component, you should see something like this:

  • <my-element _nghost-c0>
  • <p _ngcontent-c0>
  • Notice that I have an ngcontent attribute. And, that both my ngcontent
  • attribute and my parent's nghost attribute both end with "-c0". That's
  • the local-scoping of the CSS right there!
  • </p>
  • </my-element>

Notice that both the "nghost" and the descendant "ngcontent" attributes all end with "-c0". This suffix will change with every rendered component, keeping the component's styles contained within the current component context.

Now, when it comes to routable child views that are injected into the current view based on the browser URL state, Angular does not add the "ngcontent" to the injected element even though it is contained within a host view:


 
 
 

 
 Injected views dont have ngcontent attributes in Angular 4.4.4. 
 
 
 

As you can see, the "my-app" element has an "_nghost-c0" attribute; and, all of its local descendants have the corresponding "_ngcontent-c0" attribute. But, the injected view only has its own "host" attribute - no "ngcontent" attribute. As such, if we want to target the injected view component from within our parent component's CSS, we have to lean on the "::ng-deep" selector and the child combinator (>) to override the view styles:

  • :host {
  • display: block ;
  • font-size: 16px ;
  • }
  •  
  • a {
  • color: red ;
  • cursor: pointer ;
  • text-decoration: underline ;
  • }
  •  
  • /*
  • When Angular injects the routable ng-component into the current component view,
  • it DOES NOT ADD the typical "ngcontent-XYZ" HTML attribute that's used to prevent
  • styles from bleeding outside of the current component. This means that any attempt
  • to use a "naked" View selector in this component CSS will fail to affect an injected
  • component. As such, we have to use the DEEP SELECTOR with the CHILD COMBINATOR (>)
  • in order to target the injected component.
  • --
  • NOTE: We're using the child combinator to ensure we don't accidentally bleed our
  • ::ng-deep styles beyond the scope of the current component.
  • */
  • :host ::ng-deep > layout-a {
  • border-color: gold ;
  • }
  •  
  • :host ::ng-deep > layout-b {
  • border-color: aqua ;
  • }

The ::ng-deep selector prevents the "[_ngcontent-c0]" selector from being added to the remainder of the current selector path. And, the (>) selector prevents the given CSS from being applied to anything internal to the injected view. Together, these two selectors allow us to override the border-color of the injected, routable view component:


 
 
 

 
 Using the ng-deep selector, we can target injected view components in Angular 4.4.4. 
 
 
 

As you can see, this leaves us with a CSS selector of:

[_nghost-c0] > layout-b

... which has a higher CSS specificity than the injected view component:

[_nghost-c2]

This allows the "my-app" component to override the border-color of the injected view component without messing up any of the internal styles of the injected view.

The simulated encapsulation functionality for component styling in Angular 4 is pretty awesome. I feel like it maintains the easy, informal nature of CSS without having to deal with the complexity of the "CSS in JavaScript" movement. That said, there are some facets of simulated encapsulation that are fuzzy. Like, how to we deal with the deprecation of "deep selector" in the CSS specification? At least with the ::ng-deep selector, we can still do things like apply styles to injected, routable views in our Angular 4.4.4 components.



Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

@Jason,

Hmmmm, that's interesting. The last time I read about it, I thought that Angular was going to provide ::ng-deep as the mid-term solution between the deprecation of the /deep/ selector and whatever the future specification will hold. If they are planning to deprecate ::ng-deep, then I am not sure what people can do. This has wide reaching implications. Without some ability to do deep selection, you basically can't have any global CSS that may override something more locally (such as a border color or a background color).

Someone needs to answer this question more satisfactorily! For now, I'll continue to use the ::ng-deep selector, however, since it works currently.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.