62

I have DOM that looks something like this:

<app>
    <router-outlet></router-outlet>
    <project>...</project>
</app>

where project element is inserted by the router.

How do I add a class to this element?

Kugel
  • 19,354
  • 16
  • 71
  • 103

14 Answers14

71

Assuming you always want the class applied to this component, you can use host in your component metadata:

@Component({
  selector:'project',
  host: {
      class:'classYouWantApplied'
  }
})

Resulting in:

<app>
    <router-outlet></router-outlet>
    <project class="classYouWantApplied">...</project>
</app>
Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
drew moore
  • 31,565
  • 17
  • 75
  • 112
23

use the adjacent sibling selector and the * wildcard to select the element immediately following the router-outlet


styles.css

router-outlet + * {
  /* your css */
}

enter image description here

JED
  • 1,538
  • 2
  • 19
  • 47
  • 4
    As far as I know Angular scopes the elements with a `[_nghost_c1]`-like attribute, like `router-outlet + *[_nghost_c1]`. This is also added to my css (I am compiling scss, maybe that's the reason), and it does not get applied to the following element that is scoped in another way. – Francesco Pasa Nov 22 '18 at 08:00
  • 4
    You can use something like `::ng-deep router-outlet.router-flex + * { display: flex; flex: 1 1 auto; flex-direction: column }` and define it in the component. AND DON'T ANYBODY DARE SAY ITS DEPRECATED. – Simon_Weaver Mar 02 '19 at 01:48
  • 2
    Very clever! However, in my case it did not work perfectly, because I am using animation transitions between routes. During the transition, there are two components siblings of the ``````. The incoming and the outgoing. To solve that, simply replace the '+' by a '~'. That is ```router-outlet ~ *```. – kontiki May 21 '19 at 06:54
  • Works perfectly. Thanks – Elron Aug 08 '19 at 00:17
13

The key is /deep/ keyword:

    :host /deep/ router-outlet + project {
        display: block;
        border: 10px solid black;
    }

This works without any extra configurations.

:host /deep/ router-outlet + * for whatever component dynamically created by Angular Router.

Edited 2018/3/5:

Since Angular 4.3.0 made /deep/ deprecated, its suggested alternative is ::ng-deep. And there were a long discussion about this.

Val
  • 21,938
  • 10
  • 68
  • 86
5

You can use the adjacent sibling selector

router-outlet + project { ... }

https://developer.mozilla.org/en/docs/Web/CSS/Adjacent_sibling_selectors

but only if @drewmoore's approach doesn't apply.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
5

You can do this with a HostBinding, which is effectively the same as using the host property that has already been mentioned, although that method throws a TSLint error with default listing rules.

In your component on which you want to apply a class:

import { Component, HostBinding, Host (optional for typing) } from '@angular/core';

@Component({...})
export class GiveMeAClassComponent {
    @HostBinding('class.some-class') someClass: Host = true;
    ...
}

And then in your root styles.scss file, you can add the following:

.some-class {
    // Styles in here will now be applied to your GiveMeAClassComponent at a root level
}
lordchancellor
  • 3,847
  • 4
  • 26
  • 26
5
<app>
  <div class="your css class">
   <router-outlet></router-outlet>
</div>
</app>

This works for me

Awinash jaiswal
  • 281
  • 3
  • 5
  • This doesn't work, the added element from the router still exists and will mess up any flex you want to use for example. – ed__ Oct 28 '22 at 01:03
5

Currently, Angular 6 recommends me to use @HostBindings and @HostListeners instead of the host property:

export class ProjectComponent {
  @HostBinding('class') classes = 'classYouWantApplied';
}
Dmytro K.
  • 169
  • 3
  • 5
4

since router injects the component after the the router-outler element, if we would like to style all injected component with the same set of rules the folowing rule is can be helpful.

the css "+" operator select the first sibling element of a certain type, while asterisk (*) is used as a wild card to select any 1st sibling of router-outlet

router-outlet + * {
  // your rules
}
Shay
  • 1,245
  • 7
  • 14
3

If you need to add a class conditionally, you can add it programmatically from the component:

constructor(private renderer: Renderer2, private elemRef: ElementRef) {
  if(someCondition){
    renderer.addClass(elemRef.nativeElement, 'myClass');
  }
}
adamdport
  • 11,687
  • 14
  • 69
  • 91
2

For me it helped to wrap the router-outlet into another container.

<div class="classYouWantApplied">
  <router-outlet>
</div>

Like this you could apply the class to the surrounding container.

Rias
  • 1,956
  • 22
  • 33
1

I created a RouterOutletHelperDirective which can be modified as necessary.

Your use-case may be different but for me :

  • I needed to set a default set of classes on each router-outlet generated item
  • I needed to prevent this default based on certain conditions, such as ActivatedRoute data.

You use it like this (the class is optional):

<router-outlet routerOutletHelper
               [routerOutletHelperClass]="'blue-border'"></router-outlet>

Then create the directive, add and export it to your app module.

import { Directive, ElementRef, Renderer2, Input } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { Subscription } from "rxjs";

@Directive({
    selector: 'router-outlet[routerOutletHelper]'
})
export class RouterOutletHelperDirective
{
    constructor(private routerOutlet: RouterOutlet,
                private element: ElementRef<HTMLElement>,
                private renderer: Renderer2) { }

    subscription = new Subscription();

    @Input('routerOutletHelperClass')
    customClassName: string | undefined;

    ngOnInit() 
    {
        this.subscription.add(this.routerOutlet.activateEvents.subscribe((_evt: any) => {

            // find the component element that was just added
            const componentElement = this.element.nativeElement.nextSibling;

            // add a custom class
            if (this.customClassName) 
            {
                this.renderer.addClass(componentElement, this.customClassName);
            }

            // add my default classes, unless the activated route data 
            // (specified in module routing file) has { addDefaultClasses: false }
            if (this.routerOutlet.activatedRouteData && this.routerOutlet.activatedRouteData.addDefaultClasses !== false)
            {
                // these are my application's default classes (material / theming)
                // (an additional data parameter could be 'darkTheme: boolean')
                this.renderer.addClass(componentElement, 'mat-typography');
                this.renderer.addClass(componentElement, 'rr-theme-light');
            }
        }));
    }

    ngOnDestroy()
    {    
        this.subscription.unsubscribe();
    }
}
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • this no longer works with ivy because the new node is added after the existing node whereas before it was added before – Simon_Weaver Feb 11 '20 at 00:40
0

Add host class name which will add a class to the component then use adjacent to target the element.

@Component({
  selector:'project',
  host: {
      class:'Project-wrapper'
  }
})

now use CSS adjacent with angular

::ng-deep to target it:
::ng-deep .Project-wrapper {}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
0

it's simple, let's say in your app component you have a
<router-outlet ></router-outlet>

inside this router-outlet you have a route component called <app-product-detail></app-product-detail>

and you want to change the view of in the app.component.html.

first inside the the component <app-product-detail> add this snippet:

@Component({


selector: 'app-product-detail',
  host: {
    class:'Project-wrapper'
},
  templateUrl: './product-detail.component.html',
  styleUrls: ['./product-detail.component.css']
})```

class= 'could be any name'

inside app.component.css you add this snippet below:

    ::ng-deep .Project-wrapper { 
width: 85%;
        background-color: aqua;
    }
Lewis99
  • 31
  • 1
  • 4
-1

I know it's late.

I created this directive to allow you to set (static) or bind (dynamic) classes to each component that is injected by Angular next to the router-outlet element:

https://www.npmjs.com/package/ngx-router-outlet-class

Please upvote my feature request if you DO think it's necessary to have such a built-in functionality.

KingMario
  • 161
  • 2
  • 10