innerHtml doesn't work with ionic 2 pipes, how to? - javascript

I have a simple pipe that returns some html
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'rating',
pure: false
})
export class RatingPipe implements PipeTransform {
transform(value: any): string {
let stars = "<ion-icon name='star'>" + value + "</ion-icon>";
return stars;
}
}
the problem is when i use it, i get nothing
// this works fine
<p><span innerHtml="{{'<h1>some text</h1>'}}"></span></p>
// if I add a pipe, it doesn't work
<p><span innerHtml="{{variableFromControler | rating}}"></span></p>
// if I add a pipe, it doesn't work
<p><span [innerHtml]="variableFromControler | rating"></span></p>
any ideas?
One solution
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizationService } from '#angular/platform-browser'; // to become DomSanitizer
#Pipe({
name: 'rating',
pure: false
})
export class RatingPipe implements PipeTransform {
sanitizer: any;
constructor(private domSanitizationService: DomSanitizationService) {
this.sanitizer = domSanitizationService;
}
transform(value: any): string {
value = parseInt(value);
let stars = '';
for(let i = 1; i <= 5; i++) {
stars += i <= value ? "<ion-icon class='ion-ios-star'></ion-icon>" : "<ion-icon class='ion-ios-star-outline'></ion-icon>";
}
return this.sanitizer.bypassSecurityTrustHtml(stars);
}
}
documentation on DomSanitizationService

It won't work with this html
"<ion-icon name='star'>" + value + "</ion-icon>"
because ion-icon is a ionic-angular component and it should load via angular2 instead of just using innerHTML.
Anyway you should use DomSanitanizeService for your html pipe like this:
#Pipe({
name: 'rating',
pure: false
})
export class RatingPipe implements PipeTransform {
constructor(private domSanitizer: DomSanitizationService){}
transform(value: any): string {
let stars = "<div>" + value + "</div>";
return this.domSanitizer.bypassSecurityTrustHtml(stars);
}
}
And in your html you have to use property binding:
<span [innerHtml]="text | rating"></span>
I would leverage a custom icon wrapper for your case:
#Component({
selector: 'ion-rating-icon',
template: '<ion-icon name="star">{{rating}}</ion-icon>'
})
class RatingIconComponent {
#Input() rating;
}
Then use it like:
<ion-rating-icon [rating]="text"></ion-rating-icon>
See all examples in Plunker

Related

Search Exact String using custom pipe in Angular 8

I have created a custom pipe in order to filter my data. This is my pipe code
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filterAll'
})
export class SpecialPipe implements PipeTransform {
transform(value: any, searchText: any): any {
if(!searchText) {
return value;
}
return value.filter((data) => JSON.stringify(data).toLowerCase().includes(searchText) );
}
}
This filter is totally working well, but my requirement is to search the exact string. I have to return the values begins with my searchText (not included). I have also tried .startsWith(), which is also not working. Is there any way to achive this?
startsWith is what you're looking for.
Here's the working snippet:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter',
})
export class FilterPipe implements PipeTransform {
transform(items: any[], filterdata: string): any[] {
if (!items) return [];
if (!filterdata) return items;
filterdata = filterdata.toString().toLowerCase();
return items.filter((it) => {
return it.name.toLowerCase().startsWith(filterdata);
});
}
}
Working StackBlitz demo

How to bind statically defined HTMl content and click Event returned from java service to angular component

I am using angular 5 and java 8 for my web application. I am having a java service which return html content with angular click event. The same i am binding to angular component. The html content is working but click event is not working.
Below is the sample code from Java
#RequestMapping(value="/hitSample",method = RequestMethod.GET)
public String hitSample() {
StringBuilder sb = new StringBuilder();
sb.append("<a (click)=\"callSampleFunction()\"><p>This is a paragraph.</p> A Tag end
</a>");
sb.append("<p>This is a paragraph. 2 </p>");
return sb.toString();
}
SampleComponent.ts
import { Component, OnInit } from '#angular/core';
import {ViewReviewService} from '../../services/view-review-service';
import {Response} from '#angular/http';
#Component({
selector: 'app-sample-hit',
templateUrl: './sample-hit.component.html',
styleUrls: ['./sample-hit.component.css']
})
export class SampleHitComponent implements OnInit {
sampleData: any;
constructor(private viewReviewService: ViewReviewService) { }
ngOnInit() {
this.hitSample();
}
hitSample() {
this.viewReviewService.hitSample().subscribe((res: Response) => {
console.log(res['_body']);
this.sampleData = res['_body'];
});
}
callSampleFunction() {
alert('got call');
}
}
sampleComponent.html
<div [innerHTML]="sampleData" > </div>
I want callSampleFunction() to be triggered on click.
You can use the same way you've used <div [innerHTML]="sampleData" > </div> but you need to sanitise the string template for security reasons.
<div [innerHtml]="sampleData | safeHtml">
SafeHtmlPipe.ts
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 this.sanitizer.bypassSecurityTrustHtml(sanitizedContent);
}
}

remove special character with directive not work angular

i want transform text in uppercase and remove special character of input text using directive.
the directive is this:
import { Directive, EventEmitter, Output, Input, OnInit, ChangeDetectorRef } from '#angular/core';
#Directive({
selector: '[uppercase]',
host: {
'[value]': 'uppercase',
'(input)': 'format($event.target.value)'
}
})
export class UppercaseDirective implements OnInit {
#Input() uppercase: string;
#Output() uppercaseChange: EventEmitter<string> = new EventEmitter<string>();
constructor(private cdr: ChangeDetectorRef) {
}
ngOnInit() {
this.uppercase = this.uppercase || '';
this.format(this.uppercase);
}
format(value) {
let regNumber = /^\d+$/;
if (!value || regNumber.test(value)) return
let valueValido = value.replace(/[`~!##$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '');
valueValido = valueValido.toUpperCase();
this.uppercaseChange.next(valueValido);
this.cdr.detectChanges();
console.log(valueValido)
}
}
}
the input continue show special caracter, but in log i can see the value, and it not have more special caracter.
i use '' to replace, and not work, but if i use any caracter or space to replace ex: ' ', it work, but i need replace with no value ''. How i can do it?
#Edit
the solution not work, the code:
<div [ngSwitch]="input.controlType">
<div class="ui-g-12" *ngSwitchCase="'textbox'" [class.esconder]="input.hidden" >
<label for="input.key">{{input.label | doisPontos}}</label><br>
<input [formControlName]="input.key" [id]="input.key" [type]="input.type" [ngStyle]="{'width':input.width}"
pInputText [(uppercase)]="form.controls[input.key].value" [class.ui-state-error]="form.controls[input.key].touched && form.controls[input.key].invalid "
[value]="form.controls[input.key].value | removeEspecialCaractere" >
</div>
the pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'removeEspecialCaractere'
})
export class removeEspecialCaracterePipe implements PipeTransform {
transform(value: any, args?: any): any {
return value === undefined ? '' : value.replace(/[`~!##$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').toUpperCase();
}
}
For Text Transform use Pipe. place the pipe transform where you want to remove special character
html
{{ p | transform}}
transform.pipe.ts
transform(value: any, args?: any): any {
return value === undefined ? '' : value.replace(/[`~!##$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').toUpperCase();
}
Example:https://stackblitz.com/edit/angular-wp12pu

Angular, DomSanitizer, bypassSecurity script

I'm playing with bypassSecurityTrust* functions of Angular. Goal is to get a script tag to execute on the page. But it either keeps sanitizing with the message
WARNING: sanitizing HTML stripped some content
or I see in the console a
SafeHtmlImpl {changingThisBreaksApplicationSecurity: "<script>alert(1)</script>.
Goal is to get this working.
What I currently use and tried:
#Pipe({ name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value: string): string {
console.log(this.sanitized.sanitize(SecurityContext.NONE, value))
return this.sanitized.sanitize(SecurityContext.NONE, value);
}
}
#Component({
selector: 'app-demo',
templateUrl: './demo.component.html',
styleUrls: ['./demo.component.css']
})
export class DemoComponent implements OnInit {
name: string;
html: string;
constructor(private sanitizer: DomSanitizer) {
this.name = 'Angular2';
this.html = "<script> alert(8) </script>";
}
ngOnInit() {
}
}
and the template html:
<div [innerHtml]="html | safeHtml"></div>
I tried both sanitize with SecurityContext.NONE which should work looking at the code and bypassSecurityTrustHtml(value). The above code was inspired by this answer.
Any ideas on how to execute that JavaScript?
So yes, innerHtml can't insert script tags, but it doesn't stop it from one of the many other ways to inject JavaScript.
Working example:
import { Component, Pipe, PipeTransform, SecurityContext} from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser'
#Pipe({ name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value: string) {
console.log(this.sanitized.bypassSecurityTrustHtml(value));
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
#Component({
selector: 'app-demo',
template: `
<div [innerHtml]="html | safeHtml">
</div>
`
})
export class DemoComponent {
html: string;
h_html: string;
constructor(private sanitizer: DomSanitizer) {
this.html = "<svg onload=\"alert(1)\"> blah </svg>"
this.h_html = sanitizer.sanitize(SecurityContext.HTML, "<svg onload=\"alert(2)\"> blah </svg>');
}
}
What doesn't work is
return this.sanitized.sanitize(SecurityContext.HTML, value);
or using
<div [innerHtml]="h_tmpl"></div>
Not sure why. Should behave the same afaiu.

Use AngularJS 2 Component like a function in the element attribute

Hi guys!
I'm learning AngularJS 2 for a while now and now creating my own app based on Laravel 5 REST API. Anyway - that isn't very important atm.
What is important is that I want to provide the translation for the whole application and I found an issue that is hard to solve for me.
So - from the beginning... I'm created my ResourcesService that's translating the string:
getTranslation ( key: string, replace: Array<TranslationReplace> = null, locale: string = null, fallback: boolean = null ): Observable<Resource> {
var params = "key=" + key +
( replace ? "&replace=" + JSON.stringify(replace) : '') +
( locale ? "&locale=" + locale : '') +
( fallback ? "&fallback=" + fallback : '');
var headers = new Headers({'Content-Type':'application/x-www-form-urlencoded'});
return this.http.post(this.apiUrl + 'getTranslation', params, {headers: headers})
.map(this.extractData)
.startWith({ name: 'Loading...', value: 'Translating...' })
.catch(this.handleError);
}
And I created a TranslateComponent that's providing the translation, here's the whole component:
import {Component, Input, Injectable, OnInit, OnChanges, SimpleChange} from "#angular/core";
import {ResourcesService} from "../services/resources.service";
import {TranslationReplace} from "../models/TranslationReplace";
#Component({
selector: 'translate',
template: `{{translation}}`
})
#Injectable()
export class TranslateComponent implements OnInit, OnChanges {
#Input() ref: string;
#Input() replace: Array<TranslationReplace>;
#Input() locale: string;
#Input() fallback: boolean;
private translation: string;
constructor(private resourcesService: ResourcesService) {}
ngOnInit() : void {
this.getTranslation();
}
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
for (let propName in changes) {
if(propName == 'replace') {
this.getTranslation();
}
}
}
private getTranslation(): void {
this.resourcesService.getTranslation(this.ref, this.replace, this.locale, this.fallback).forEach(translation => this.translation = translation.value );
}
}
All is working just perfect and to call for the translation I have to simply call the selector like that:
<translate [ref]="'string.to_translate'"></translate>
But...
Now I'd like to use the translation in the attribute.
So I found the ugly way to achieve it by creating the reference of the translation and the call it in the attribute. But it's very nasty...
First of all I need to add this bit to my template:
<translate [ref]="'string.to_translate'" style="display:none;" #myStringTranslation></translate>
And next in my element call it and ask for the property by the reference:
<input type="text" [(ngModel)]="input" #input="ngModel [placeholder]="myStringTranslation.translation">
And I really don't like the idea.
What I'm looking for is to call it somehow, I don't know... emit it? And make it looks better. Don't create extra elements.
So my question is:
Can I do it better? Can I somehow call the translation directly from the attribute without the reference?
** ----- UPDATE ----- **
Ok, I learn my lesson :) Thanks to Meir for showing me the right direction and also the Angular.io site for the tutorials.
So finally I added a TranslateDirective to my application:
import {Directive, Input, ElementRef, OnChanges, OnInit, SimpleChange, Renderer} from "#angular/core";
import {TranslationReplace} from "../models/TranslationReplace";
import {ResourcesService} from "../services/resources.service";
#Directive({
selector: '[translate]'
})
export class TranslateDirective implements OnInit, OnChanges {
#Input('translate') ref: string;
#Input('translateReplace') replace: Array<TranslationReplace>;
#Input('translateLocale') locale: string;
#Input('translateFallback') fallback: boolean;
#Input('translateAttr') attr: string;
private translation: string;
constructor(
private elRef: ElementRef,
private renderer: Renderer,
private resourcesService: ResourcesService
) {}
ngOnInit():void {
this.getTranslation();
}
ngOnChanges(changes: {[propKey: string]: SimpleChange}):void {
for (let propName in changes) {
if(propName == 'replace') {
this.getTranslation();
}
}
}
private getTranslation(): void {
if(this.attr)
this.resourcesService.getTranslation(this.ref, this.replace, this.locale, this.fallback).forEach(translation =>
{
this.translation = translation.value;
this.renderer.setElementAttribute(this.elRef.nativeElement,this.attr,this.translation);
});
}
}
And now can easily add the translations to the attributes like that:
<input type="text" [(ngModel)]="input" #input="ngModel [translate]="'string.to_translate'" [translateAttr]="'placeholder'">
Thanks for your help!!
You can turn it into an attribute directive:
#Directive({
selector: 'translate'
})
export class TranslateDirectiev {
#Input() translate: string;
constructor(private elRef: ElementRef){}
ngOnChanges(changes: SimpleChanges): void {
if(this.translate){
var translatedText: string = translateSvc.translate(this.translate);
this.renderer.setElementProperty(this.elementRef.nativeElement, 'innerHTML', translatedText);
}
}
}
This is a simple example without the service injection. Also, for input fields you might need to have a different approach and update the value attribute and not the innerHtml

Categories

Resources