Angular add different style to a JSON - 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)

Related

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.

Working with custom components for input generates "No value accessor for form control with path X->0->Y"

I have a working form taking the following HTML markup. No errors or warnings.
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
I transformed it into a custom component, which also works, as shown below.
<app-input-text [info]="'Title'"
formControlName="targetField"
ngDefaultControl></app-input-text>
In my next view, I need to use FormArray as follows - still working code.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
</div>
</div>
Now, I expected that combining both (i.e. being able to use custom input component and being able to form array for components) would post no problem. However, the sample below doesn't work.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<app-input-text [info]="'Title'"
formControlName="targetField"
class="col-sm-6"></app-input-text>
</div>
</div>
It generates the following error.
No value accessor for form control with path: 'stuff -> 0 -> targetField'
The custom component is design like this (although given that it works in the explicit markup example, I'm not sure if it's relevant information). The only (wild) guess I have might be that value isn't jacked into the form array field somehow.
export class InputTextComponent implements OnInit {
constructor() { this.value = new EventEmitter<string>(); }
#Input() info: string;
#Output() value: EventEmitter<string>;
onEdit(value: any): void { this.value.emit(value); }
}
The group and array creating in the current view is done like this (not sure if this is of any relevance neither, as it works for the explicit HTML markup case).
this.form = builder.group({
id: "",
stuff: builder.array([
builder.group({ targetField: "aaa" }),
builder.group({ targetField: "bbbb" }),
builder.group({ targetField: "cc" })
])
});
Is there a limitation in Angular in this regard that I'm not aware of? I'm rather sure there's not and that I'm just doing something fairly clever simply missing a tiny detail.
I do understand the error but I can't see how it relates to the code. The form can't find the 0th element in the array or that element has no field of that name. Since I do get to see a few rows, I know there must be a 0th element. Since I specified the name of the field, I know there is indeed such. What else am I missing?

Strip html in Angular template binding

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!

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

Issue with ng-bind-html

I'm trying to create a website using angular-js .I'm using rest api calls for getting data. I'm using ngSanitize as the data from rest call includes html character. Even if i use ng-bind-html in my view the html tags are not removed .What is the mistake in my code.Can anyone help me
var app = angular.module('app',['ngSanitize','ui.bootstrap']);
app.controller("ctrl",['$scope','$http','$location','$uibModal',function($scope,$http,$location,$uibModal,){
//here im making my rest api call and saving the data in to $scope.items;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-sanitize.js"></script>
<body ng-app="app" ng-controller="ctrl">
<div class="hov" ng-repeat ="item in items">
<br/>
<div>
<hr class="divider" style="overflow:auto;">
<ul>
<li style="font-family: Arial, Helvetica, sans-serif;font-weight:900px;">
<h3>Name<span style="color:#0899CC;" ng-model="id2" ng-bind-html="item.name"></span></h3>
</li>
<li>
<h4>Description: <span ng-bind-html="item.description"></span></h4>
</li>
</ul>
</div>
</div>
</body>
So you want to allow HTML tags or deny them ?
If you want the html to be escaped in the data coming from your server, just use ng-bind. It will replace < with < and > with > thus showing the HTML tags instead of computing them.
If you want to completely remove any HTML tags
Try this filter
filter('htmlToPlaintext', function() {
return function(text) {
return text ? String(text).replace(/<[^>]+>/gm, '') : '';
};
}
);
then in your HTML :
<h3>Name<span style="color:#0899CC;" ng-model="id2" ng-bind-html="item.name | htmlToPlaintext"></span></h3>
If you trust the source and want to compute the HTML it send you
You can use this filter
app.filter('trusted', function($sce){
return function(html){
return $sce.trustAsHtml(html)
}
});
then in your HTML :
<h3>Name<span style="color:#0899CC;" ng-model="id2" ng-bind-html="item.name | trusted"></span></h3>
And
<h4>Description: <span ng-bind-html="item.description | trusted"></span></h4>
I have the same problem before some time , then i created a filter for this problem, You can use this filter to do sanitize your html code:
app.filter("sanitize", ['$sce', function($sce) {
return function(htmlCode) {
return $sce.trustAsHtml(htmlCode);
}
}]);
in html you can use like this:
<div ng-bind-html="businesses.oldTimings | sanitize"></div>
businesses.oldTimings is $scope variable having description of strings or having strings with html tags , $scope.businesses.oldTimings is the part of particular controller that you are using for that html.
see in the snapshot:
you can use limitHtml filter to do the same:
app.filter('limitHtml', function() {
return function(text, limit) {
var changedString = String(text).replace(/<[^>]+>/gm, ' ');
var length = changedString.length;
return changedString.length > limit ? changedString.substr(0, limit - 1) : changedString;
}
});
Then you can add bothe filter in your html like that:
<p class="first-free-para" ng-bind-html="special.description| limitHtml : special.description.length | sanitize">
Hope it will work for you.