Strip html in Angular template binding - html

I have a list displaying data that can sometimes contain HTML
<li *ngFor="let result of results">
<span [innerHTML]="result.question.title">
</span>
</li>
The problem with using innerHTML is that the HTML gets parsed and rendered, so things like <p> tags will add margins and ruin the list's alignment.
I would like to strip all html tags and just output plain text.
An approach like this:
<li *ngFor="let result of results">
<span>
{{result.question.title}}
</span>
</li>
does not strip the HTML, it just outputs the HTML as plain text.
How can I strip the HTML and leave plain text the 'Angular' way?

I guess there is no direct way to strip HTML tags from string, you can use Pipe, write a "Pipe" like this:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'striphtml'
})
export class StripHtmlPipe implements PipeTransform {
transform(value: string): any {
return value.replace(/<.*?>/g, ''); // replace tags
}
}
then add "StripHtmlPipe" to your module "declarations", after these steps you can use this pipe in your HTML:
<li *ngFor="let result of results">
<span>
{{result.question.title | striphtml}}
</span>
</li>
please note that the code above is not fully tested.

I wouldn't recommend using a regex to parse html as suggested by
kite.js.org. Use the browsers textContent / innerText function instead:
htmlToText(html: string) {
const tmp = document.createElement('DIV');
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || '';
}
This should be much more reliable. You can still use a pipe if you like, just don't use regex to parse html!

Related

angular 2 does not parse correctly html code

Trying to HTML code to view the resulting HTML using the innerHTML property, but as you can see in the example below, it doesn't. The HTML code is viewed as the HTML tags and only, instead of creating the elements it renders them as simple text.
https://codepen.io/Dralius/pen/OJzoZxm
#Component({
selector: 'my-app',
template: `
<span [innerHTML]='working'> </span>
<span [innerHTML]='notWorking'> </span>
`
})
class AppComponent {
working="<h1>hello world angular 6</h1>";
notWorking='<p> Random Text </p>'
constructor() {
// TODO: Define your Angular component implementation
}
}
an idea can be to parse the notWorking string into valid html with domparser (for sample) before inject it in innerHTML
https://codepen.io/jeremy-denis/pen/rNpZKzO?editors=1111
const { Component, VERSION } = ng.core;
#Component({
selector: 'my-app',
template: `
<span [innerHTML]='working'> </span>
<span [innerHTML]='notWorking'> </span>
`
})
class AppComponent {
working="<h1>hello world angular 6</h1>";
notWorking='<p> Random Text </p>'
constructor() {
this.notWorking = new DOMParser().parseFromString(this.notWorking, 'text/html').body.innerText;
}
}
This won't work just because of one little problem : It just won't. You are not using HTML tags, only the "symbols" of them. What is your problem exactly here? using < is mostly used for printing out the literal symbol on the page, not as HTML tag.

Angular innerHTML contents are jumbled. Only the last innerHTML is displayed correctly

In my Angular project (version 8) I am creating a list of static HTML from database and rendering it in parent HTML. Only the last div having innerHTML is rendered correctly, all the preceding divs having child html is not rendered correctly. The contents are jumbled. Basically the child html's style is not honored except for the last child html.
I am using sanitize html pipe for the div.
The angular component onInit queries DB in a loop. Each get call returns HTML text which is appended to an array of strings. The HTML text is basically PDF to HTML converted file. Each of the HTML file has its own style tag.
My guess is that only the last innerHTML's style is applied to all the preceding child innerHTML hence the jumbled contents (unless my guess is incorrect)
Any suggestion to solve the issue ?
HTML
<div *ngFor="let qBank of tsqm.selectedQuestions; let i = index">
<div class="page">
<div [innerHTML]="questionDataFromHtml[i] |
sanitizeHtml"></div>
</div>
</div>
Sanitize HTML:
#Pipe({ name: 'sanitizeHtml'})
export class SanitizeHtmlPipe implements PipeTransform {
constructor(private _sanitizer: DomSanitizer) { }
transform(value: string): SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(value);
}
}
Component:
ngOnInit(){
this.questionset = this.storage.get(quesId);
//pseudo code
forEach(item in this.questionset){
this.getHTMLfromDB(item)
}
}
getHTMLfromDB(question: QuestionBank) {
this.Service.getQuestionHtmlFile(question.questionFilePath).subscribe(res =>
{
this.questionDataFromHtml.push(res.text());
question.questionData.questionDataFromHtml = res.text();
});
Correct display. Question1 and Question2 are same
Correct display
Incorrect display. Question1 and Question2 are different
Incorrect display
Stackblitz:
stackblitz
The issue is all the css styling is overridden and the final values are applied.
Use id/class to apply the style to specific component.
I've made changes to your stackblitz example. Check here
In hello.component.ts
Applied red color to the text using text-red id.
export class HelloComponent {
#Input() name: string;
html1 =
"<html><head><style> #text-blue {color:blue;}</style></head><body><h2 id='text-blue'>Inner HTML1 in red</h2></body></html>";
html2 =
"<html><head><style> #text-red {color:red;}</style></head><body><h2 id='text-red'>Inner HTML2 in blue</h2></body></html>";
}
I solved this issue by using iFrame tag and srcdoc attribute. The backend service will return html text to angular. After DOM sanitizing the html documents are displayed in the iFrames.

Angular add different style to a JSON

From BE it returns a json and I show it, but I would like, for example, "type, includeDeactive, code etc..." be bold and the (other) value, after the colons (:) in italics.
Example:
body: { "**type**: { **includeDeactive**: *false*, **code**: *null*, **address**: *null*, **name**: *null*, }
I've already formatted the json with the <pre> tag and I pass it data with the data binding {{detail.body | json }}. This is my html:
<mat-label style="font-family: Roboto">
<strong>Body</strong>:
<span style="font-family: Roboto">
<pre id="formatJson"> {{detail.body | json }} </pre>
</span>
</mat-label>
This is my function in ts:
objBody: any;
parsedObj: any;
constructor(
private dialogRef: MatDialogRef<DettaglioAuditComponent>,
#Inject(MAT_DIALOG_DATA) public data,
private auditService: AuditService,
) { }
ngOnInit(): void {
this.detail = {};
this.auditService.getDetails(this.data.element).then(
(value) => {
this.detail = value;
var objBody = JSON.stringify(this.detail.body);
var parsedObj = JSON.parse(objBody);
//objBody = objBody.replace('{"', '{"<b>'); - already tried but doesn't work!
//objBody = objBody.replace('":', '</b>":');
this.loading = false;
},
(error) =>
});
}
Any idea? Thanks in advance!
If you need it for professional purpose and "just want it to work", use Highlightjs or an equivalent library. The output will be fancy and very readable :
If you want to do it with Highlightjs
You need highlightJs of course. You can use the npm dependency ngx-highlight-js which will enable you to use HighlightJs in an Angular project.
In the Angular project, you can include the content like this :
<textarea highlight-js ngNonBindable lang="javascript">
// include your content here
</textarea>
You MUST include the content in a <textarea> tag (for indentation preservation)
highlight-js is of course the directive that will tell HighlightJs to do its work
ngNonBindable prevents Angular from trying to interpret the content of the textArea as Angular code
lang="javascript" : you can optionally specify the language for accurate coloration
Also, don't forget to import the HighlightJsModule module where necessary
If you want to do it by yourself
(or understand how things work under the hood)
You can JSON.parse() the content of your string, recursively iterate on the given object keys and generate HTML out of it.
Indentations and newlines can be rendered by placing the generated html in a <pre> pr <textarea> tag
Colors can be achieved by placing colored elements in dedicated <span class="[...]"> tags
On the previous example, the HTML code generated by HighlightJS is :
<pre><code class="language-json hljs">[
{
<span class="hljs-attr">"title"</span>: <span class="hljs-string">"apples"</span>,
<span class="hljs-attr">"count"</span>: [<span class="hljs-number">12000</span>, <span class="hljs-number">20000</span>],
<span class="hljs-attr">"description"</span>: {<span class="hljs-attr">"text"</span>: <span class="hljs-string">"..."</span>, <span class="hljs-attr">"sensitive"</span>: <span class="hljs-literal">false</span>}
},
{
<span class="hljs-attr">"title"</span>: <span class="hljs-string">"oranges"</span>,
<span class="hljs-attr">"count"</span>: [<span class="hljs-number">17500</span>, <span class="hljs-literal">null</span>],
<span class="hljs-attr">"description"</span>: {<span class="hljs-attr">"text"</span>: <span class="hljs-string">"..."</span>, <span class="hljs-attr">"sensitive"</span>: <span class="hljs-literal">false</span>}
}
]
</code></pre>
Relevant pieces of code :
Convert the content to an object with JSON.parse(contentAsString)
Check if a given property is an array with Array.isArray(obj)
Iterate on object keys with Object.keys(obj)

Having a date inside a div with innerHTML content

I have a <div> and I want to have the date inside it. (For some reasons I can't change this configuration) This box may have a text with html properties. It is clear that I should use [innerHTML] but when using it, I can't put the date inside the <div>. I'm wondering if there is any way to have the text without showing the properties like <br/> and having a line break instead, and also having the date just inside the div?
Here is my StackBlitz
text = 'Hi<br/>How are you?';
<div>{{ text }}</div>
<hr/>
<div>{{ text }}<span>HH:MM</span></div>
<hr/>
<div [innerHTML]="text"></div>
<hr/>
<div [innerHTML]="text"><span>HH:MM</span></div>
The snippet is not working because it is not Angular. Please refer to my code above.
You could replace your <br> tags with \n and then add CSS to preserve the line break:
div {
white-space: pre-wrap;
}
<div>{{ text }}</div>
text = 'Hi\nHow are you?';
To put html content to the div, it is needed to convert the html code to stay safe from XSS using DomSanitizer.
So it will be good to generate a new pipe to do this action as follows.
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
import DOMPurify from 'dompurify';
#Pipe({
name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
constructor(protected sanitizer: DomSanitizer) {}
public transform(value: any, type: string): any {
const sanitizedContent = DOMPurify.sanitize(value);
return angular.bypassSecurityTrustHtml(sanitizedContent);
}
}
And on the main module, you can import this pipe and can use it as follows.
<div [innerHTML]="text | safeHtml"></div>

Reactjs: How to put HTML into primaryText of a list item?

I would like to have a span inside the ListItem like this:
<ListItem
primaryText={"<span class='inner'>Some important info</span>" + item.title}
/>
When this is rendered, I don't get an HTML span element, but a text <span class='inner'>Some important info</span>Title of the list item. How to make HTML render as HTML?
Remove "" around the span, because when you use " it will get converted into string, it will not be treated as html tag.
Write it like this this:
primaryText={<div>
<span className='inner'>Some important info</span>
{item.title}
</div>}
Note: class is reserved keyword so, to apply css classes use className.
EDIT: Ignore me, just saw you needed it specifically for a ListItem
If you need to render HTML within an element, you can use the dangerouslySetInnerHTML prop (but it comes with some risks, and the name suggests):
function createMarkup() {
return {__html: 'First ยท Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
Docs here:
https://facebook.github.io/react/docs/dom-elements.html#dangerouslysetinnerhtml
Based on the info given, you should move the span inside the ListItem component and deal with it there rather than passing in the props.
<ListItem
primaryText={ 'Some important info' }
title={ item.title }
/>
//List Item Component
import React from 'react'
const ListItem = ( props ) => {
return (
<li>
<span className='inner'>{ props.primaryText }</span>{ ` ${props.title}` }
</li>
)
}
export default ListItem