Having a date inside a div with innerHTML content - html

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>

Related

Angular, How to display text without the HTML tags?

I have a string stored in my localStorage with the HTML tags since I use ngx-quill (Angular Rich Text Editor) which stores the data in HTML formatted text.
This is what my localStorage looks like:
As you can see from the picture above, in description, there is p tags.
Is there any way I can display it on Angular page without the tags?
Use: [innerHTML] for that, for example:
<div [innerHTML]="data.description"></div>
You need to use innerHTML and make sure that you trust the HTML if it contains script tag else Angular will throw an error.
Do the following, create a safeHTML pipe:
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
#Pipe({ name: 'sanitizeHtml' })
export class SanitizeHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(html: string): any {
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}
and then use this with innerHTML like this:
<span [innerHTML]="description" | sanitizeHtml"></span>

Angular 10 Load SVG Dynamically Without IMG or OBJECT Tags

I'm working on a simplified way to load SVG files without using IMG or OBJECT tags as it impedes my ability to control fill colors through external CSS. Using inline SVG is ideal, but with so many components using repeated icons, it's a lot of maintenance and I'd prefer to centralize them in their .svg file format. I thought about just making each one their own component, but that means there's a component.ts file I don't need for each one, and it might be a little confusing or other developers.
So far, creating a custom element that pulls the svg location from a "src" attribute is working:
#Component({
selector: 'app-svg',
template: `
<ng-template>
{{ src }}
</ng-template>
<span [innerHTML]="svg"></span>
`
})
export class SvgComponent implements OnInit {
svg: SafeHtml = '';
#Input() public src = '';
constructor(private http: HttpClient, private sanitize: DomSanitizer) {
}
ngOnInit(): void {
this.http.get(this.src, {responseType: 'text'}).subscribe(svg => {
this.svg = this.sanitize.bypassSecurityTrustHtml(svg);
});
}
}
Then I use my custom element in another component.html:
<app-svg src="assets/test.svg"></app-svg>
The result of course is an inline SVG with an inline element as a wrapper:
<app-svg src="assets/test.svg" ng-reflect-source="assets/test.svg">
<span>
<svg>
<path d="...">
</svg>
</span>
</app-svg>
I suppose this is harmless enough, but it's a little annoying and there's unnecessary extra markup. Ideally, I'd want to have the innerHTML applied to APP-SVG, but that means the svg in the binding would need to exist outside of the TS for for the custom element due to scoping issues. It's also messy having to remember to include [innerHTML] on every APP-SVG tag. I've tried using [outerHTML] on the SPAN tag in the template, but I get a runtime error saying there is no parent container element.
So, my question is can this work?:
Replace the in the template with the loaded SafeHtml? Or,
Apply the loaded SafeHtml as the innerHTML of the selector in the SvgComponent TS? Or,
Use <svg [innerHTML]="svg"> as part of the template instead of SPAN, but remove the parent SVG from the loaded SafeHtml before applying it to the innerHTML? Or,
Is there something in NPM that already does what I'm trying to create?
I wish they made this easier. Any advice or explanation as to why this won't work would be greatly appreciated. Thank you!
Naturally, as SOON as I post my question, I trip over the solution. The trick is to use ElementRef so that I can target the selector's innerHTML, and I don't have to use DomSanitizer to do it. The new code looks as follows (including imports this time):
import {Component, OnInit, Input, ElementRef} from '#angular/core';
import {HttpClient} from '#angular/common/http';
#Component({
selector: 'app-svg',
template: `
<ng-template>
{{ src }}
</ng-template>
`
})
export class SvgComponent implements OnInit {
#Input() public src = '';
constructor(
private el: ElementRef,
private http: HttpClient,
) {}
ngOnInit(): void {
this.http.get(this.src, {responseType: 'text'}).subscribe(svg => {
this.el.nativeElement.innerHTML = svg;
});
}
}
If you don't want to have app-svg as a container, you can use instead:
this.el.nativeElement.outerHTML = svg;
And it will replace app-svg with he loaded svg. Hope this helps anyone else trying to accomplish the same thing. Cheers!

How to render a raw HTML on Angular

I've tried to render a raw HTML using innerHTML, as bellow:
<span *ngIf="displacyHTML " [innerHTML]="displacyHTML"></span>
This HTML has style in line, but it does not work in that way.
The HTML is rendered, but the style does not.
If I paste the same raw HTML into a separate file it works perfectly.
The styles I mention is used basically to change the background color of the mark tags.
Potentially, you need a SafePipe for your html as your browser does not trust injected html code:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({ name: 'safePipe'})
export class safePipe implements PipeTransform {
constructor(protected sanitizer: DomSanitizer):{}
transform(value) {
return this.sanitizer.bypassSecurityTrustHtml(value);
}
}
usage in HTML:
<span [innerHtml]="potentiallyNotSafeHtmlCode | safePipe"></span>

How to access html elements of component tag?

I want to access plain HTML declared in my component tag. Suppose I have component
#Component({
selector: 'app-demo'
template: '<some_template></some_template>'
})
export class AppDemoComponent {
}
if I am defining h1 inside the tag in another component
<app-demo>
<h1> demo text </h1>
</app-demo>
How can I access the h1 element inside the AppDemoComponent?
Edit:
This question is not about ViewChild as ViewChild gets information from the current template of the component. I'm asking if the component tag is called in the different file and the tag has HTML elements then how to access it.
Use ElementRef
You can use ElementRef to access the current component reference, allowing you to query it for nested elements.
getElementsByTagName, querySelectorAll, and getElementsByClassName will look down into the nested elements as they operate by inspecting what's rendered in the DOM, ignoring any encapsulation Angular does.
I am not sure if there is an Angular specific way to do it, but using vanilla JS lets you get there.
Child Component
import { Component, OnInit } from "#angular/core"
#Component({
selector: 'app-demo-child'
template: `
<h1> demo text </h1>
<h1 class="test"> demo text2 </h1>
<h1> demo text3 </h1>
`
})
export class AppChildComponent {
}
Parent Component
import { Component, OnInit, ElementRef } from "#angular/core"
#Component({
selector: 'app-demo-parent'
template: `
<app-demo-child></app-demo-child>
`
})
export class AppParentComponent {
constructor(
private elRef:ElementRef
) { }
doStuff() {
let HeaderElsTag = this.elRef.nativeElement.getElementsByTagName('h1') // Array with 3 h3 elements
let HeaderElsQuer = this.elRef.nativeElement.querySelectorAll('h1') // Array with 3 h3 elements
let HeaderElsClass = this.elRef.nativeElement.getElementsByClassName('test') // Array with 1 h3 element
}
}
Warning, this will look indiscriminately within your component, so be careful that you don't have nested elements with the same class name otherwise you'll have some hard to debug side effects
You can use content children. for your reference please follow the link below:
content children vs view children

Angular DomSanitizer not binding angular component

I am trying to add html content dynamically into a DIV. Statically this works nicely.
Code which works:
<popover-content #pop1
title="Cool Popover"
placement="right"
[closeOnClickOutside]="true">
Popped up!!!
</popover-content>
<div>
<span>Testing with <span [popover]="pop1" [popoverOnHover]="true">popover</span> as they are not working with DomSanitizer</span>
</div>
Now I need to generate this div content in the backend and then have to dynamically add this inside the div.
Code which doesn't work:
HTML:
<popover-content #pop1
title="Cool PopOver"
placement="right"
[closeOnClickOutside]="true">
Popped up!!!
</popover-content>
<div [innerHtml]="message | safeHtml">
</div>
.ts file:
this.message = '<span>Testing with <span [popover]="pop1" [popoverOnHover]="true">popover</span> as they are not working with DomSanitizer</span>'
Pipe:
import {Pipe, PipeTransform} from '#angular/core';
import {DomSanitizer} from '#angular/platform-browser';
#Pipe({
name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {
}
transform(value) {
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
After this also the popover component was not getting called.
While inspecting, I did see that, for dynamically added innerHtml content to DIV, angular is not adding some special behavior to the tag attributes. Why so?
And how can I make it work?
With [innerHTML]="..." you can add HTML to the DOM, but Angular won't care what HTML it contains, except for sanitization.
Angular components, directives, event and property bindings only work for HTML added statically to a components template.
What you can do is to compile the HTML with a components template at runtime like explained in How can I use/create dynamic template to compile dynamic Component with Angular 2.0?