Reputation: 1998
I have an Angular component that uses PrismJS for syntax highlighting code blocks. The component is as follows:
import { Component, AfterViewInit, Input,
ElementRef, ViewChild, ChangeDetectorRef } from '@angular/core';
declare var Prism: any;
@Component({
selector: 'prism',
template: `
<div hidden="true" #rawContent>
<ng-content></ng-content>
</div>
<section class="code-container">
<pre><code [innerHtml]="content" class="block language-{{language}}"></code></pre>
</section>
`
})
export class PrismComponent implements AfterViewInit {
@Input() language: string;
@ViewChild('rawContent') rawContent: ElementRef;
content: string;
constructor(public cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.content = Prism.highlight(this.rawContent.nativeElement.textContent.trim(),
Prism.languages[this.language]);
this.cdr.detectChanges();
}
}
The issue is that when I use it, I have to manually escape any invalid HTML characters.
I've tried using DomSanitizer
.sanitize() on both the component element reference value and the rawContent
reference value in the following locations in order to try to circumvent this:
ngOnChanges()
ngOnInit()
ngAfterViewInit()
Angular throws invalid character errors before any of these events take place when the code block contains invalid HTML characters.
How would I go about sanitizing the code block specified in rawContent
in order to prevent manually escaping invalid HTML characters?
Example StackBlitz Solution
Upvotes: 3
Views: 1065
Reputation: 34445
As of angular 5.2, this is currently not possible because of the way single brackets are interpreted. Using ngNonBindable
does not work with single brackets
https://github.com/angular/angular/issues/11859
This will fail
<div>
Test content with a bracket {
</div>
To include strings with backets in your template, you need to assign the string to a component's variable
//component.ts
snippet = `export class Demo {
id: number;
name: string;
}`;
//component.html
<sanitized-prism [snippet]="snippet" language="typescript"></sanitized-prism>
import {
Component,
AfterViewInit,
Input,
ChangeDetectorRef,
SecurityContext
} from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
declare var Prism: any;
@Component({
selector: 'sanitize-prism',
template: `
<section class="code-container">
<pre><code [innerHtml]="content" class="block language-{{language}}"></code></pre>
</section>
`
})
export class SanitizePrismComponent implements AfterViewInit {
@Input() snippet: string;
@Input() language: string;
content: SafeHtml;
constructor(public cdr: ChangeDetectorRef, public sanitizer: DomSanitizer) { }
ngAfterViewInit() {
this.content = this.sanitizer.bypassSecurityTrustHtml(
this.sanitizer.sanitize(
SecurityContext.HTML,
Prism.highlight(this.snippet, Prism.languages[this.language])));
this.cdr.detectChanges();
}
}
If you really want to hard code strings with brackets in your template file, then you can use of these solutions:
Encode brackets
export class Demo { id: number; name: string; }
(which is what you already had)
Wrap everything in an interpolated expression
{{"export class Demo { id: number; name: string; } }"}
Note: if the string contains douoble quotes, you need to escape them with \"
{{" let: str = \"string\"; "}}
Escape brackets (as suggested by angular's error message)
export class Demo {{'{' }} id: number; name: string; {{ '}'}
Wrap your code in CDATA (@Juan Mellado's suggestion)
<prism language="typescript">
<![CDATA[ export class Demo { id: number; name: string; } ]]>
</prism>
The last one if the cleanest way IMHO
Upvotes: 0
Reputation: 15113
Use CDATA (Character Data):
<prism language="typescript">
<![CDATA[ export class Demo { id: number; name: string; } ]]>
</prism>
Upvotes: 2