1

I am attempting to create a reusable angular2 component that accepts an array of URLs to html files on my server and creates a content window with tabs to switch between "chapters", effectively swapping out the html and css inside the content window. I have tried all sorts of things including iframes but those don't work, the angular 1 ng-include work-arounds that I can find on StackOverflow but they have all since been deprecated, and the closest I've got is building a component that you can @Input html and it interpolates the content but style won't apply and angular strips out any style or script tags. Here is what I have tried.

In my parent component class:

htmlInput: string = "<h1>Why Does Angular make this so hard?</h1>";
cssInput: string = "h1 { color:red; }"

Parent Component HTML:

<app-html [html]='htmlInput' [css]='cssInput'></app-html>

My HTML Component:

import { Component, Input, OnInit } from '@angular/core';

@Component({
    selector: 'app-html',
    template: '<div [innerHtml]=html></div>', //This works but no style
    //template: '{{html}}', //This displays the actual markup on page
    styles: ['{{css}}'] //This does nothing
    //styles: ['h1 { color: red; }']//Also nothing
})
export class HtmlComponent implements OnInit {
    @Input() html: string = "";
    @Input() css: string = "";
    ngOnInit() {

    }
}

The result of this code is

Why Does Angular make this so hard?

But no red color. Maybe style is applied before the innerHtml is added to DOM? I don't know but just putting {{html}} results in displaying the actual markup with the h1 tags visible.

The reason I want to do it this way is that I have a bunch of HTML pages already created sitting in a folder on my server from before I angularized my site that all share a single style sheet. I'd like to just be able to flip through them like pages in a book without reloading the page and since there are so many and I'm likely to add more all the time, I'd really rather not create routing for every single one. (I already have routing for basic site navigation.)

Does anybody have a better suggestion for how to embed styled HTML into a page dynamically in the most recent version of Angular 2? At the time of this post we are in 2.0.0-beta.17.

OR... I already figured I may be approaching this issue from the entirely wrong angle. There must be a reason Angular is making this so difficult and deprecating all the solutions people have come up with so If anyone has a suggestion about how I could achieve the same results in a more angular friendly way I'd love to hear that too.

Thank you.

Edit:

I was able to fix my issue by creating a pipe which sanatizes the html before adding it to an iframe.

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

@Pipe({ name: 'safe' })
export class SafePipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {}
  transform(url: string) {
    return this.sanitizer.bypassSecurityTrustResourceUrl(url);
  }
}

And then you can just pass your html into the iframe.

<iframe width="100%" height="1000" frameBorder="0" [src]="url | safe"></iframe>

This is useful to me since I have some old pages that use all sorts of jquery and style etc. This works as a quick fix to have them show up.

Rohan Fating
  • 2,135
  • 15
  • 24
Riggy
  • 77
  • 1
  • 1
  • 9

2 Answers2

2

Angular2 rewrites the styles added to a component by including the dynamically added attributes like _ngcontent-yle-18 into the CSS selectors.

Angular2 uses this to emulate shadow DOM style encapsulation. These attributes are not added to dynamically added HTML (for example with innerHTML).

Workarounds

  • add styles to index.html because these styles are not rewritten by Angular2

  • set ViewEncapsulation.None because then Angular doesn't add the encapsulation emulation attributes

  • use /deep/ to make Angular2 ignore the encapsulation emulation attributes

See also Angular 2 - innerHTML styling

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • I was able to apply the style by putting it in index.. I'm not in love with the solution but I am reading more about the other solutions you posted and the other SO post you provided.Do you know if I will have any issues using pretty print or highlight js? There are some html pages that have code snippets and I'd like to apply syntax coloring. – Riggy Nov 06 '16 at 14:54
  • Not sure what you mean. What issues do you mean? I wouldn't expect issues. – Günter Zöchbauer Nov 06 '16 at 14:56
  • those libraries dynamically add classes to your or
     tags after the page loads in order to facilitate syntax coloring. It doesn't seem to want to work on innerHtml. I guess I'm really looking for a way to have an internal section of my page ignore all angular rules where I can use script tags with jQuery and custom styles and all that. This way I just just stick in pieces of my old non-angular website where I want them. Am I just dreaming too big?
    – Riggy Nov 06 '16 at 15:05
  • You just have to consider a few things. You can't add the script tag to a components template because it will removed. There are other ways available (don't know if this applies to your use case) You can't add the styles to the component if you use default view encapsulation, because they will rewritten. There are always workarounds, it depends on what you exactly want to do. – Günter Zöchbauer Nov 06 '16 at 15:09
  • Well, I'll give you the answer for providing a working solution for styling. And I'll keep looking for another work around that solves all my use cases. I may come back and edit my question if I find a solution so it wont be the same information as the other Question you posted. Thank you. – Riggy Nov 06 '16 at 15:14
0

You should wrap your css into an object and use ngStyle to bind it to your component rather than the styles attribute, because styles does not support data binding.

Example:

htmlInput: string = "<h1>Why Does Angular make this so hard?</h1>";
cssInput: string = "{h1 { color:red; }}"

Parent Component HTML:

<app-html [html]='htmlInput' [css]='cssInput'></app-html>

Your HTML Component:

import { Component, Input, OnInit } from '@angular/core';

@Component({
    selector: 'app-html',
    template: '<div [innerHtml]="html" [ngStyle]="css"></div>',
    styles: []
})
export class HtmlComponent implements OnInit {
    @Input() html: string = "";
    @Input() css: string = "";
    ngOnInit() {

    }
}
Supamiu
  • 8,501
  • 7
  • 42
  • 76
  • EXCEPTION: Uncaught (in promise): Error: Error in ./HtmlComponent class HtmlComponent - inline template:0:24 caused by: Cannot find a differ supporting object '{h1 { color:red; }}' – Riggy Nov 06 '16 at 14:32
  • Check the above response, but seems like you need to disable view encapsulation and set it to None. – Supamiu Nov 06 '16 at 14:42