How to show icons from FontAwesome in Angular's innerHTML content? - json

I have Font Awesome in my Angular 14 app implemented as described here: https://www.npmjs.com/package/#fortawesome/angular-fontawesome . I use <fa-icon [icon]="faIconName"> semantics. I also use few languages on my website and I load translations through json files. In templates I use semantics like <span [innerHTML]="here_i_have_key_to_translate | translate | safeHtml" where I use translate service and pipe which let me show in template forbidden code like fa-icon (default this is earased from json html, just like 'style' attribute). How can I use Font Awesome icons in json? Below there is example of json, code.
language json from assets\i18n:
"key_1":"<p><b>example text</b><br /><fa-icon [icon]=\"faPhoneSquare\"></fa-icon> +43 123 123 923</p>",
"key_2":"<p><fa-icon [icon]=\"faPhoneSquare\"></fa-icon> +44 123 123 123</p>",
"key_3":"<p><fa-icon [icon]=\"faPhoneSquare\"></fa-icon> +43 123 123 123</p>",
template:
<section>
<h4 [translate]="'header_1'"></h4>
<p>
<fa-icon [icon]="faEnvelope" class="me-2"></fa-icon> Some static text with icon on left which displays fine
</p>
</section>
<section [innerHTML]="'key_1' | translate | safeHtml"></section>
<section [innerHTML]="'key_2' | translate | safeHtml"></section>
<section [innerHTML]="'key_3' | translate | safeHtml"></section>
typescript:
import { Component } from '#angular/core';
import { TranslationAppService } from '../../../common/services';
import { faPhoneSquare, faEnvelope } from '#fortawesome/free-solid-svg-icons';
#Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.scss']
})
export class ContactComponent {
faPhoneSquare = faPhoneSquare;
faEnvelope = faEnvelope;
}

This isn't how Angular is supposed to work.
Angular is in charge of handling your HTML. You are not supposed to write HTML templates that you send to Angular. It simply does not work because it's not supposed to work like that.
Just write your HTML in your HTML files and only provide the data you need from your API (in this case the phone number it seems).

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.

How to use a variable in ts as a tagname in a HTML file? [duplicate]

This question already has answers here:
Angular HTML binding
(24 answers)
Closed 3 years ago.
I want to know if there is any way to use the HTML tag name (<p> for e.g.) which is obtained from a variable?
The following is the code I tried:
app.component.ts
import { Component,OnInit } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
name = 'Angular';
somevalues = [1, 2, 3, 4, 5]
tagName;
getFromCharCode(index) {
return String.fromCharCode('A'.charCodeAt(0) + index);
}
ngOnInit(){
this.tagName = "p";
}
}
app.component.html
<div *ngFor="let x of somevalues; let i = index">
{{x}} - {{i}}
{{ getFromCharCode(i) }}
<h1>{{tagName}}
</h1>
</div>
If I tried like:
<{{tagName}}></{{tagName}}>
I'm getting error like
Template parse errors:
Unexpected closing tag "{{tagName}}". It may happen when the tag has already been closed by another tag.
I referred to this, but I find it is pretty complex for a simple replacement. Is there any other way to achieve this?
EDIT-1:
Many of you suggest to use innerHTML but that would be feasible incase of small contents. In my typical case, I would like to have all my html content in the same file and I would get only the name of the tag in ts file
You can use innerHTML for this:
ngOnInit(){
this.tagName = "<p></p>";
}
<div [innerHTML]="tagName"></div>
Hi first of all this way will not work as the interpolation considers the value as a string so it will always appear as a text on the screen.
By your question what i understand is you want to add HTML inside an already existing element.
The easiest way way would be: -
in your ts give your variable the value that you want to insert so eg.
tagName = `<div> Some text inside </div>`;
and then in your html you can simply do:-
<h1 [innerHTML]="tagName">
other way to do this would be to get the reference of the element in ts and insert it from there
Try this one
<div *ngFor="let x of somevalues; let i = index">
{{x}} - {{i}}
{{ getFromCharCode(i) }}
<h1 innerHtml="<{{tagName}}></{{tagName}}>"></h1>
</div>

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?

Which Component Should the Angular 2 Security Bypass rule be placed in?

I am pretty new to Angular 2 and I am trying to display bunch of album covers. The path to images are returned by the server as a part of an Album object. I ran into the usual sanitizing issue.
I read about the solution here: Angular 2, 2.0.0-rc.2, Cannot apply inline css3 transform with style directive
here: Angular2 - WARNING: sanitizing unsafe style value url(SafeValue must use [property]=binding:
and here: https://angular.io/docs/ts/latest/guide/security.html#!#bypass-security-apis
However since the overall project structure is not mentioned in either of these cases, I am having a little bit of trouble figuring out where (which file?) and how I should be adding the bypass security rule. I tried every possibility I could think of but all of them throws an error. Logically, it should be added to the component generating the template, however it might be that it doesn't have direct access to the Album object.
The code base was formed by following the tutorial at www.angular.io and making the necessary changes.
Project Structure:
project
| index.html
| style.css
|--app
| main.ts
| app.component.ts
| app.component.spec.ts
| app.module.ts
| app.routing.module.ts
| albums.component.ts
| album-dashboard.component.ts
| album.service.ts
| album.ts
| album-detail.component.ts
| dashboard.component.html
| album.component.html
| album.detail.component.html
Relevant HTML and TS files are below. If any other file from above is relevant to the question I would be happy to update
dashboard.component.html: The html where the albums are being displayed. Please don't suggest using some information is gonna sit on the top and usually that is harder to deal with actual imgs. I am aware style = "..." is not the correct syntax, changing it to what is shown in the threads create a lot of errors I wasn't able to trace back
<div class="grid grid-pad">
<a *ngFor="let album of albums" [routerLink]="['/detail', album.id]" class="col-1-4">
<div class="module album" style="background-image: url('{{album.path}}');">
<!-- <h4>{{album.name}}</h4> -->
</div>
</a>
</div>
album.ts
export class Album {
id: number;
name: string;
artist: string;
path: string;
}
album-dashboard.component.ts: Please notice that this is a component for a collection of album. As a side question the Threads I have linked to above either add a static rule or dynamically add a rule for the current instance, does this imply I have to iterate through each instance in the collection and add them one by one ?
import { Component, OnInit } from '#angular/core';
import { Album } from './album';
import { AlbumService } from './album.service';
#Component({
moduleId: module.id,
selector: 'album-dashboard',
templateUrl: 'dashboard.component.html',
styleUrls: [ 'dashboard.component.css' ]
})
export class DashboardComponent implements OnInit {
albums: Album[] = [];
constructor(private albumService: AlbumService) { };
ngOnInit(): void {
this.albumService.getAlbums()
.then(albums => this.albums = albums);//.slice(1, 4));
}
}
app.component.ts
import { Component } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'my-app',
template: `
<nav>
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<a routerLink="/albums" routerLinkActive="active">Albums</a>
</nav>
<router-outlet></router-outlet>
`,
styleUrls: ['app.component.css'],
})
export class AppComponent {
title = 'Production Crew';
}
Any help is appreciated!
Fixed. For anyone having similar issues,
please see: Angular2 dynamic background images
and: In RC.1 some styles can't be added using binding syntax
Turns out style.background-image has been 'black-listed' against xss attacks however style.background and ngStyle properties will work.
I don't have an explanation on why these two provide the same result but are not considered not secure yet. Will update if I can get an explanation

Styled HTML content dynamically switched with tabs using Angular 2

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.
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
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() {
}
}