cssangularangular-renderer2

Angular: How to add global CSS, but only for one specific page?


How can I add separate CSS for one page in Angular?

I've discounted both of the standard solutions to this problem:

  1. Using ::ng-deep or ViewEncapsulation.None. CSS added in this way won't be removed when navigating away from the component, so this won't work for us.

  2. Adding a class to the <body> via ngOnInit/ngOnDestroy. The CSS I want to add can't be attached to the <body>, so this isn't good either.

Example CSS:

@media print{
    @page{
        margin:0;
    }
    body{
        margin:30px;
    }
}

I've created a Stackblitz which explains the problem clearly.

Maybe the question would be better summarized as "How to add global CSS, but remove it when we leave the page?".


Solution

  • Solved!

    We print the <style> block directly into the component's HTML, and therefore when the component gets removed, our <style> block gets removed too.

    First, create a new component to handle the work:

    component.ts: (Only this. You don't need an HTML or style.css file)

    // Usage Instructions:
    // Inside  your local component, place this HTML 
    // <app-local-css [style]="'body{background:green !important;}'"></app-local-css>
    // OR
    // <app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>
    
    @Component({
      selector: "app-local-css",
      template: '<span style="display:none" [innerHTML]="this.safeString"></span>'
    })
    export class LocalCSSComponent implements OnInit {
      constructor(protected sanitizer: DomSanitizer) {}
      @Input() scriptURL?: string;
      @Input() style?: string;
    
      safeString: SafeHtml;
      ngOnInit() {
        if (this.scriptURL) {
          let string = '<link rel="stylesheet" type="text/css" href="' + this.scriptURL + '">';
          this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
        } else if (this.style) {
          let string = '<style type="text/css">' + this.style + "</style>";
          this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
        }
      }
    }
    

    And then use it like this:

    mySample.component.html:

    <app-local-css [style]="'body{background:green !important;}'"></app-local-css>
    // OR
    <app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>
    

    Here's a StackBlitz.

    (Note that without this component, printing a <style> block directly into the HTML wouldn't work because Angular would sanitize it away, moving it into the <head>, which would prevent it from getting deleted when the component is gone. Here it works correctly because of DomSanitizer.bypassSecurityTrustHtml.)