0

So I've got an Angular app that has pretty basic routing, and I'm using flexbox to layout my components. The issue I can't figure out is with code like this:

  <div fxLayout="column"
       fxFlex
       class="layout__right">
    <router-outlet></router-outlet>

  </div>

The child component that is routed to contains the following in its SCSS:

:host {
    @include make-flex-container(column);
    flex: 1;
}

That make-flex-container just applies some flexbox related styles, and works fine in many places of the app. What's happening in my case though is when routing to this particular child component I see a style tag applied to the ng-component element created by Angular. What's causing my problem is for some reason the style includes flexbox items that are overriding what I'm putting in :host:

enter image description here

You can see in the screenshot that my :host styles are being applied, but the styles on the ng-component tag are simply overriding them. For the life of me I can't understand why there's a specific style tag added here, so where the content within it would come from. Does anyone know why Angular would put style tags on the HTML generated for router-outlets? When I navigate to other components at this same routing level this style tag isn't present.

I assume this is an issue with my code, but I just can't run down where to look given what I see.

UPDATE

Here's a minimal reproduction of the issue on Stackblitz:

https://so-48451609-routing-flex-issue.stackblitz.io

In that example you can see how the element created next to the router outlet has styles applied to it. The only dependency added there is @angular/flex-layout, so it's gotta be doing something to cause this.

Sam Storie
  • 4,444
  • 4
  • 48
  • 74
  • all rendered router outlets I have seen so far had some angular class on them , thought I am not sure from where exactly they do come from, they never caused any problem in the render ... does that somehow interfere with other styles of your app? – Kristian Jan 25 '18 at 20:51
  • Yeah, in this case I'm trying to use a column layout in the child component (via the :host{} code), but that style is forcing the component into a row layout. – Sam Storie Jan 25 '18 at 20:53
  • By the chance are you not using the Angular Material? – Kristian Jan 25 '18 at 21:20
  • Yes, I'm using Angular Material 5.1.0 – Sam Storie Jan 25 '18 at 21:22
  • 1
    I'm actually starting to wonder if it's somehow related to my use of @angular/flex-layout since the added styles are so specific to flexbox... – Sam Storie Jan 25 '18 at 21:32

3 Answers3

1

The fxLayout directive applies styles to child elements. It does this on the element itself to not interfere with other styles.

The @angular/flex-layout library's static API has directives that work either on a DOM container or DOM elements. fxLayout is an example of the former, fxFlex is an example of the latter.

See the docs here, note the two sections for elements and containers: https://github.com/angular/flex-layout/wiki/Declarative-API-Overview#api-for-dom-containers

joh04667
  • 7,159
  • 27
  • 34
  • Thanks for the answer, but I'm not clear on what exactly you're suggesting. I am using flex-layout, and I know it's considered beta, but there's nothing I've seen yet to indicate that I'm using it incorrectly. Now I'm not sure there's isn't a bug somewhere, but it shouldn't be applying any styles to the router-outlet elements from what I can tell. – Sam Storie Jan 26 '18 at 15:05
  • Please see my update with a super simple example of this with only @angular/flex-layout as a dependency. – Sam Storie Jan 26 '18 at 18:34
0

THEY COME FROM VIEW ENCAPSULATION

I believe that these classes are part of view encapsulation that prevents angular elements from being styled from outside and to prevent interference between view components. At least that is what they say in official docs @angular.io. As suggested @this SO post the '_ngcontent-c0' is naming of the first component within the host.


ANGULAR MATERIAL IS CAUSING TROUBLES

I have found a bunch of issues related to encapsulation at GitHub and they often seem to be related to using Angular Material, which seems to have encapsulation turned off sometimes somehow. Such as described here. Hence I conclude that your implementation of Angular Material might be at fault. For in depth analysis of what the mechanism of this unwanted interaction is, you might probably like to start by reading joh04667's answer to your question since it provides links to the documentation of the particular material you use.


PROPER STYLING

Common solution to those issues is overriding styles with /deep/ selector as described in both above shared links. A number of unofficial articles on styling Angular app is available as well, such as this one.


IDEA

So the idea is to overwrite the styles in your component with /deep/ like this:

:host /deep/ .your_flex_class {
    @include make-flex-container(column);
    flex: 1;
}

CONSIDER VALID CSS

Since I think that you use Scss, using /deep/ selector should be ok and should be compiled successfully (I use it quite often to style inline SVGs). However, in a fact, this selector seems to be deprecated as a whole bunch of things related to the issue of Shadow DOM. I believe the correct universal approach would be to use 'shadow-piercing descendant combinator':

:host >>> .your_flex_class {
    @include make-flex-container(column);
    flex: 1;
}

It should does all the same as /deep/ selector. To explain the mechanism, let me cite from official docs for CSS Scoping Module Level 1

When a >>> combinator (or shadow-piercing descendant combinator) is encountered in a selector, replace every element in the selector match list with every element reachable from the original element by traversing any number of child lists or shadow trees.

Basically, it does not care about the wrapper and bites down to your target to style it. So you have even one more option to try ...

Kristian
  • 501
  • 2
  • 13
  • Thanks for the answer @Emocuc, but I'm not using any ViewEncapsulation anywhere in my own code, so I don't quite understand how that's creating an issue. I'm also not using any /deep/ selectors in my own code...just :host selectors to help style child components. – Sam Storie Jan 26 '18 at 15:07
  • @SamStorie - ViewEncapsulation is by default part of Angular ecosystem. It is, let say, built-in feature. By default, all Angular components, including those related to router-outlet. Unless you have may quite specific changes, ViewEncapsulation effects all your code. The /deep/ selector should be used in your code in order to overwrite the unwanted behaviour of your mixed styles. – Kristian Jan 26 '18 at 15:28
  • Ok, thanks for clarifying. This is a bit new to me so let me explore what you're saying and get back to this question later today. – Sam Storie Jan 26 '18 at 15:36
  • Please see my update with a super simple example of this with only @angular/flex-layout as a dependency. – Sam Storie Jan 26 '18 at 18:34
  • @SamStorie Do you mean that span with inner content 'This is some content outside the router outlet'? Can's see anything on that one -> https://pasteboard.co/H4KsZtY.png – Kristian Jan 26 '18 at 18:40
0

Ok, once I created a working example of this I eventually found the culprit and a suitable work-around. What is happening is within my router child component I'm using fxFlex from @angular/flex-layout to flex the element. I'm applying the flexbox container CSS in the :host{} section of the components CSS, but flex-layout doesn't see this in the rendered HTML and applies the style tag to do a flexbox row layout automatically.

Here's an issue on Github with my comments and a suitable workaround of using !important in my :host{} CSS:

https://github.com/angular/flex-layout/issues/496#issuecomment-360870510

Sam Storie
  • 4,444
  • 4
  • 48
  • 74