215

I am getting chunks of HTML codes from HTTP calls. I put the HTML blocks in a variable and insert it on my page with [innerHTML] but I can not style the inserted HTML block. Does anyone have any suggestion how I might achieve this?

@Component({
  selector: 'calendar',
  template: '<div [innerHTML]="calendar"></div>',
  providers: [HomeService], 
  styles: [`h3 { color: red; }`]
})

The HTML that I want to style is the block contained in the variable "calendar".

Srdjan Pazin
  • 103
  • 2
  • 5
Jakob Svenningsson
  • 4,175
  • 6
  • 23
  • 26

12 Answers12

383

update 2 ::slotted

::slotted is now supported by all new browsers and can be used with ViewEncapsulation.ShadowDom

https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted

update 1 ::ng-deep

/deep/ was deprecated and replaced by ::ng-deep.

::ng-deep is also already marked deprecated, but there is no replacement available yet.

When ViewEncapsulation.Native is properly supported by all browsers and supports styling accross shadow DOM boundaries, ::ng-deep will probably be discontinued.

original

Angular adds all kinds of CSS classes to the HTML it adds to the DOM to emulate shadow DOM CSS encapsulation to prevent styles of bleeding in and out of components. Angular also rewrites the CSS you add to match these added classes. For HTML added using [innerHTML] these classes are not added and the rewritten CSS doesn't match.

As a workaround try

  • for CSS added to the component
/* :host /deep/ mySelector { */
:host ::ng-deep mySelector { 
  background-color: blue;
}
  • for CSS added to index.html
/* body /deep/ mySelector { */
body ::ng-deep mySelector {
  background-color: green;
}

>>> (and the equivalent/deep/ but /deep/ works better with SASS) and ::shadow were added in 2.0.0-beta.10. They are similar to the shadow DOM CSS combinators (which are deprecated) and only work with encapsulation: ViewEncapsulation.Emulated which is the default in Angular2. They probably also work with ViewEncapsulation.None but are then only ignored because they are not necessary. These combinators are only an intermediate solution until more advanced features for cross-component styling is supported.

Another approach is to use

@Component({
  ...
  encapsulation: ViewEncapsulation.None,
})

for all components that block your CSS (depends on where you add the CSS and where the HTML is that you want to style - might be all components in your application)

Update

Example Plunker

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Do you know if there is a way to group more than one css element with only one :host>>> tag? I've also tried to used the :host>>> tag in an external css file but i cant get that to work. Do you know if this is possible? – Jakob Svenningsson Apr 12 '16 at 09:48
  • What do you mean by "group more than one css element"? If you add the external CSS using `styleUrls: [...]` to the component, then it should work with external styles as well. – Günter Zöchbauer Apr 12 '16 at 09:51
  • 7
    Just a note for anyone, this doesn't work either with node-sass, or with the styleUrl. Only in the styles:[...] – dessalines Jun 09 '16 at 03:41
  • @thouliha this seems to be an issue with your configuration. Angular doesn't treat `styles` and `styleUrls` differently. – Günter Zöchbauer Jun 09 '16 at 03:47
  • Note, this method does **NOT** work if you are using `angular-cli` and `SASS` or `SCSS` for your styling. You must use regular `CSS` for it to work. – morphatic Dec 08 '16 at 06:20
  • 12
    With SASS use `/deep/` instead of `>>>` – Günter Zöchbauer Dec 08 '16 at 09:11
  • What would the recommendation be for rendering the styles of attribute directives in innerHTML? For example, `el.nativeElement.style.backgroundColor = 'yellow';`, as shown in the documentation example [here](https://angular.io/docs/ts/latest/guide/attribute-directives.html). – J. Adam Connor Feb 08 '17 at 20:24
  • 1
    You can'r have directives or compinents in content added with `inneeHTML` – Günter Zöchbauer Feb 08 '17 at 20:26
  • 1
    If the HTML provided by HTTP call is big and it has inline css how will that be possible as I dont have the styles predefined, I am getting it from the inline css only @GünterZöchbauer – iniravpatel Mar 10 '17 at 08:10
  • I don't know. I would try to avoid that in the first place, and was able to do it so far myself. I use only static component templates that are fully known at build time (I'm using Dart, and with Dart this is the only supported way anyway). Perhaps you should create a new question with more details about your use case. – Günter Zöchbauer Mar 10 '17 at 08:14
  • I get `core.js:1633 ERROR cordova_not_available` in browser, after adding the import of `{ViewEncapsulation} from '@angular/core';` – farahm Apr 24 '20 at 16:03
  • 3
    Saved the day in Angular 8! Thanx. It's hard to get the question right in order to find this answer! – Pianoman Jun 21 '20 at 10:37
  • 1
    `ViewEncapsulation.None` helped to fix it in Angular 8 – Lukas G Aug 08 '20 at 18:47
19

The simple solution you need to follow is

import { DomSanitizer } from '@angular/platform-browser';

constructor(private sanitizer: DomSanitizer){}

transformYourHtml(htmlTextWithStyle) {
    return this.sanitizer.bypassSecurityTrustHtml(htmlTextWithStyle);
}
Community
  • 1
  • 1
Sahil Ralkar
  • 2,331
  • 23
  • 25
  • 7
    This does allow a text to be written in the innerHTML, but CSS still doesn't reach it. – Jeremy Thille Nov 05 '20 at 15:00
  • 1
    @JeremyThille I'm facing an issue related to what you mentioned, I'm not able to style a `
      ` list, I was using `Emulated` and `::ng-deep` but, although it works, it is deprecated. Do you know how to make the CSS reach it?
    – Jonatan Lavado Sep 06 '21 at 23:10
  • I use `encapsulation: ViewEncapsulation.None` (as described in an above answer) and then CSS reaches the sub-component – Jeremy Thille Sep 07 '21 at 06:13
  • You should improve the answer adding all necessary steps to make it work – Leonardo Rick Feb 21 '22 at 20:58
  • This does not work, I've tried. It returns this error: https://stackoverflow.com/questions/39857858/angular-2-domsanitizer-bypasssecuritytrusthtml-svg . There is also the solution – Marian07 Jan 03 '23 at 17:47
6

We pull in content frequently from our CMS as [innerHTML]="content.title". We place the necessary classes in the application's root styles.scss file rather than in the component's scss file. Our CMS purposely strips out in-line styles so we must have prepared classes that the author can use in their content. Remember using {{content.title}} in the template will not render html from the content.

Ente
  • 2,301
  • 1
  • 16
  • 34
Lyfo
  • 61
  • 1
  • 1
4

If you're trying to style dynamically added HTML elements inside an Angular component, this might be helpful:

// inside component class...
    
constructor(private hostRef: ElementRef) { }
    
getContentAttr(): string {
  const attrs = this.hostRef.nativeElement.attributes
  for (let i = 0, l = attrs.length; i < l; i++) {
    if (attrs[i].name.startsWith('_nghost-c')) {
      return `_ngcontent-c${attrs[i].name.substring(9)}`
    }
  }
}
    
ngAfterViewInit() {
  // dynamically add HTML element
  dynamicallyAddedHtmlElement.setAttribute(this.getContentAttr(), '')
}

My guess is that the convention for this attribute is not guaranteed to be stable between versions of Angular, so that one might run into problems with this solution when upgrading to a new version of Angular (although, updating this solution would likely be trivial in that case).

Trevor
  • 13,085
  • 13
  • 76
  • 99
  • This is the only solution that worked for me in Angular 9 without breaking the encapsulation of the view. I've been after this solution for quite a while now. Thanks. – Nicolas Jun 03 '23 at 18:28
3

Use the below method to allow CSS styles in innerhtml.

import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
.
.
.
.
html: SafeHtml;

constructor(protected _sanitizer: DomSanitizer) {
   this.html = this._sanitizer.bypassSecurityTrustHtml(`
        <html>
        <head></head>
        <body>
           <div style="display:flex; color: blue;">
              <div>
                 <h1>Hello World..!!!!!</h1>
              </div>
           </div>
        </body>
        </html>`);
}

Example code stackblitz

Or use the below method to write directly in HTML. https://gist.github.com/klihelp/4dcac910124409fa7bd20f230818c8d1

Sathiamoorthy
  • 8,831
  • 9
  • 65
  • 77
1

The recommended version by Günter Zöchbauer works fine, but I have an addition to make. In my case I had an unstyled html-element and I did not know how to style it. Therefore I designed a pipe to add styling to it.

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';


@Pipe({
    name: 'StyleClass'
})
export class StyleClassPipe implements PipeTransform {

    constructor(private sanitizer: DomSanitizer) { }
    transform(html: any, styleSelector: any, styleValue: any): SafeHtml {
        const style = ` style = "${styleSelector}: ${styleValue};"`;
        const indexPosition = html.indexOf('>');
        const newHtml = [html.slice(0, indexPosition), style, html.slice(indexPosition)].join('');
        return this.sanitizer.bypassSecurityTrustHtml(newHtml);
    }

}

Then you can add style to any html-element like this:

<span [innerhtml]="Variable | StyleClass: 'margin': '0'"> </span>

With:

Variable = '<p> Test </p>'
Lars
  • 43
  • 5
0

For anyone that wants just applies a certain style to innerHTML :

Follow Create a safe HTML pipe

And you can concat your HTML string with CSS style like this:

return this.sanitizer.bypassSecurityTrustHtml(value+='<style type="text/css">.image img { width: 100% }</style>');

This value is from transform(value, ...args)

Joe Lee
  • 49
  • 4
0

I went the this.sanitizer.bypassSecurityTrustHtml() route initially, and set encapsulation to ViewEncapsulation.NONE, but had 2 problems:

  1. ViewEncapsulation.NONE was causing other styling issues in my component
  2. My "safe" html didn't appear to work with css variables, ie var(--blue)

This worked for me (without having to change anything else): InsertAdjacentHTML

Template:

<div id=template></div>

Code:

ngOnInit() {
  const el = document.getElementById('template');
  el.insertAdjacentHTML('afterbegin', `<span style="color: var(--blue)">hello</span>`);
}

Disclaimer: In my case, I was parsing html from config files. You wouldn't want to go this route with user inputted html.

cs_pupil
  • 2,782
  • 27
  • 39
0

The easiest and most straight forward is to use the global styles file located in angular project src folder.

Assuming the component selector is: app-my-component

Add a class to the element hosting the innerHtml content in app-my-component template:

<div class="innerhtml-class" [innerHTML]="variable.innerHtml"></div>

Add to the global styles file:

app-my-component { 
 .innerhtml-class { 
   declaration goes here 
 } 
}
Lorka
  • 49
  • 1
  • 3
  • This only works if the rule you are writing is not overwriting some other preset rule. In our case the innerHTML comes with p tags and these are predefined by our framework. Without ng-deep, the overwriting of the rule for the p tags will not work. – Filipe Melo Apr 23 '21 at 11:04
  • @FilipeMelo can you give me an example of the received innerHTML and what you are looking to achieve; I don't think I understand what you mean?. I have used my answer to style elements within the received innerHTML which included p and span tags. – Lorka Apr 24 '21 at 13:31
0

Using inline CSS variables is an alternative solution if you have limited styles that are dynamic.

I.e.

// file.ts
someVarWithHtml = 'Hello <span class="dynamic">World</span>';

// file.ng.html
<div [style]="'--my-var: ' + value"
     [innerHTML]="someVarWithHtml"></div>

// style.css
.dynamic {
  background: var(--my-var);
}
Tom Roggero
  • 5,777
  • 1
  • 32
  • 39
0

An alternative to use CSS classes inside [innerHTML] is to declare them in global file like styles.scss. This file is specified in angular.json

        "styles": [
          "./node_modules/flexboxgrid/dist/flexboxgrid.min.css",
          "src/styles.scss"
        ],
Alin
  • 592
  • 1
  • 7
  • 21
-6

If you are using sass as style preprocessor, you can switch back to native Sass compiler for dev dependency by:

npm install node-sass --save-dev

So that you can keep using /deep/ for development.