Saltar al contenido principal

safeHtml

Paquete: @tiendana/ngx-core/ui

Pipe que marca un HTML como confiable con DomSanitizer.bypassSecurityTrustHtml, para renderizarlo con [innerHTML] sin que Angular lo sanitice.

Características​

✅ Bypass de seguridad para HTML confiable
✅ Manejo de null/undefined
✅ Integración con DomSanitizer

API​

Transform​

transform(html: string | null | undefined): unknown // SafeHtml, o '' si html es null/undefined
ParámetroTipoRetorno
htmlstring | null | undefinedSafeHtml (HTML marcado como confiable) o string vacío

Uso​

<div [innerHTML]="htmlContent | safeHtml"></div>

Ejemplo Completo​

import { Component, signal } from '@angular/core';
import { SafeHtmlPipe } from '@tiendana/ngx-core/ui';

@Component({
imports: [SafeHtmlPipe],
template: `
<div [innerHTML]="richText() | safeHtml"></div>
`
})
export class ArticleComponent {
richText = signal(`
<h1>Título</h1>
<p>Párrafo con <strong>negrita</strong> y <em>cursiva</em>.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
`);
}

Con Contenido Dinámico​

@Component({
template: `
<textarea [value]="html()" (input)="html.set($any($event.target).value)">
</textarea>

<h3>Preview:</h3>
<div [innerHTML]="html() | safeHtml"></div>
`
})
export class HtmlEditorComponent {
html = signal('<p>Edita el HTML aquí</p>');
}

Implementación​

import { inject, Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({ name: 'safeHtml' })
export class SafeHtmlPipe implements PipeTransform {
private sanitizer = inject(DomSanitizer);

transform(html: string | null | undefined): unknown {
if (html === null || html === undefined) {
return '';
}

return this.sanitizer.bypassSecurityTrustHtml(html);
}
}

Manejo de Null​

// null o undefined retorna string vacío
<div [innerHTML]="null | safeHtml"></div>
<!-- Renderiza: <div></div> -->

<div [innerHTML]="undefined | safeHtml"></div>
<!-- Renderiza: <div></div> -->

Seguridad​

⚠️ Importante: Solo usa este pipe con HTML confiable (de tu backend, editores controlados, etc.).

// ✅ Seguro - HTML de tu API
<div [innerHTML]="article.content | safeHtml"></div>

// ❌ Peligroso - Input de usuario sin sanitizar
<div [innerHTML]="userInput | safeHtml"></div>

XSS Protection​

Angular sanitiza HTML por defecto. Este pipe bypasa esa protección:

// Sin pipe - Angular sanitiza automáticamente
<div [innerHTML]="html"></div>
<!-- Scripts no se ejecutan -->

// Con pipe - Bypass de sanitización
<div [innerHTML]="html | safeHtml"></div>
<!-- Scripts SÍ se ejecutan - ¡Cuidado! -->

Casos de Uso​

CMS Content​

@Component({
template: `
<article [innerHTML]="cmsContent() | safeHtml"></article>
`
})
export class CMSArticleComponent {
cmsContent = signal('');

async ngOnInit() {
const data = await this.api.getArticle(this.id);
this.cmsContent.set(data.html);
}
}

Email Templates​

<div class="email-preview" [innerHTML]="emailTemplate | safeHtml"></div>

Rich Text Editor Output​

<div [innerHTML]="editorOutput | safeHtml"></div>

Alternativas​

Si el HTML viene de usuarios, considera:

  1. Sanitizar en backend antes de guardar
  2. Usar librerías como DOMPurify
  3. Markdown en lugar de HTML directo

Combinación con highlightedText​

// highlightedText retorna un string con <span class="highlighted-text">;
// [innerHTML] lo sanitiza y conserva el span y su clase
<div [innerHTML]="text | highlightedText:searchTerm"></div>

// No necesitas safeHtml adicional, y bypasar la sanitización
// sobre texto que viene del usuario es inseguro
<div [innerHTML]="text | highlightedText:searchTerm | safeHtml"></div>
<!-- ❌ Innecesario -->

Performance​

  • Usa pure: true por defecto (cacheo automático)
  • Solo re-evalúa cuando cambia el input
  • Lightweight - solo wrapper de DomSanitizer