angularrouter-outletangular2viewencapsulation

Applying host styles to child router outlets


I am working on an app which has a main view, and inside it renders several named child router outlets. The code is as follows:

app.component.html:

<router-outlet />

app.routes.ts:

export const routes: Routes = [
  { path: '', component: SlotsView, children: [
      {
        path: '',
        outlet: 'slotA',
        component: RouterOutlet1,
      },{
        path: '',
        outlet: 'slotB',
        component: RouterOutlet2,
      }
    ]
  },
];

slots.view.html:

This is a view with some child routes
<br>
<router-outlet name="slotA"></router-outlet>
<br>
<router-outlet name="slotB"></router-outlet>

RouterOutlet1:

I am Router outlet 1
:host{
  background-color: #f88;
}
@Component({
  templateUrl: './router-outlet-1.router-outlet.html',
  styleUrls: ['./router-outlet-1.router-outlet.less'],
  encapsulation: ViewEncapsulation.Emulated,
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true
})
// eslint-disable-next-line  @angular-eslint/component-class-suffix
export class RouterOutlet1{
}

RouterOutlet2:

I am Router outlet 2
:host{
  background-color: #88f;
}
@Component({
  templateUrl: './router-outlet-2.router-outlet.html',
  styleUrls: ['./router-outlet-2.router-outlet.less'],
  encapsulation: ViewEncapsulation.Emulated,
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true
})
// eslint-disable-next-line  @angular-eslint/component-class-suffix
export class RouterOutlet1{
}

Note that the difference between both router outlets is that router outlet 1 should have a reddish color (#f88), and router outlet 2 should have a bluish color (#88f).

This is what actually happens:

enter image description here

In DevTools I see that both host elements have the same attribute (_nghost-ng-c4243086843) enter image description here

And that's why the same style is applied to both of them.

If I change the encapsulation on both router outlets to ShadowDom, the result is rendered correctly:

enter image description here

The question:

Is this a bug in Angular which should be reported to the developers, or is there a good reason for Router outlet 1's styles to be applied to Router outlet 2?

I already did a bit of googling and haven't found such a bug report.

I tried changing the encapsulation mode.


Solution

  • UPDATE

    after checking the replicated stackblitz from the user, the below change fixes the issue!

    snippet!

    ...
    host: {
      ['style.backgroundColor']: '#f88',
    },
    ...
    

    outlet 1

    import {
      ChangeDetectionStrategy,
      Component,
      OnInit,
      ViewEncapsulation,
    } from '@angular/core';
    
    @Component({
      templateUrl: './router-outlet1.component.html',
      styleUrls: ['./router-outlet1.component.less'],
      encapsulation: ViewEncapsulation.Emulated,
      changeDetection: ChangeDetectionStrategy.OnPush,
      host: {
        ['style.backgroundColor']: '#f88',
      },
      standalone: true,
    })
    export class RouterOutlet1Component implements OnInit {
      constructor() {}
    
      ngOnInit() {}
    }
    

    outlet 2

    import {
      ChangeDetectionStrategy,
      Component,
      OnInit,
      ViewEncapsulation,
    } from '@angular/core';
    
    @Component({
      templateUrl: './router-outlet2.component.html',
      styleUrls: ['./router-outlet2.component.less'],
      encapsulation: ViewEncapsulation.Emulated,
      changeDetection: ChangeDetectionStrategy.OnPush,
      host: {
        ['style.backgroundColor']: '#88f',
      },
      standalone: true,
    })
    export class RouterOutlet2Component implements OnInit {
      constructor() {}
    
      ngOnInit() {}
    }
    

    Stackblitz Demo


    Note: This output is coming from ng-component which is not even a proper component, you can just redirect the url to accomodate both the named router outlets and the components will get rendered instead of ng-component!


    It looks like a bug but I don't think its replicable in the latest version of angular (17)

    Here is a stackblitz that shows the issue not happening!

    Stackblitz Demo

    enter image description here


    To Solve this issue, you can add a host class on the component decorator, this will ensure the class gets applied on the correct host!

    outlet 2

    import {
      ChangeDetectionStrategy,
      Component,
      OnInit,
      ViewEncapsulation,
    } from '@angular/core';
    
    @Component({
      selector: 'app-router-outlet2',
      encapsulation: ViewEncapsulation.Emulated,
      changeDetection: ChangeDetectionStrategy.OnPush,
      template: `
        I am Router outlet 2
      `,
      styles: `
        :host.outlet2 {
          background-color: #88f;
        }
      `,
      host: {
        '[class.outlet2]': 'true',
      },
      standalone: true,
    })
    export class RouterOutlet2Component implements OnInit {
      constructor() {}
    
      ngOnInit() {}
    }
    

    outlet 1

    import {
      ChangeDetectionStrategy,
      Component,
      OnInit,
      ViewEncapsulation,
    } from '@angular/core';
    
    @Component({
      selector: 'app-router-outlet1',
      template: `
        I am Router outlet 1
      `,
      encapsulation: ViewEncapsulation.Emulated,
      changeDetection: ChangeDetectionStrategy.OnPush,
      styles: `
        :host.outlet1 {
          background-color: #f88;
        }
      `,
      host: {
        '[class.outlet1]': 'true',
      },
      standalone: true,
    })
    export class RouterOutlet1Component implements OnInit {
      constructor() {}
    
      ngOnInit() {}
    }
    

    Stackblitz Demo