Saltar a contenido

Componente: Sandbox de Reporte

Selector: app-reporte-sandbox

Ubicación: src/app/views/analytics/components/reporte-sandbox

Acceso: Interno — no es una ruta, lo montan GestionReportesComponent y VistaReporteComponent


¿Qué hace?

Renderiza HTML no confiable de forma aislada. Es el componente donde vive la Capa 1 (iframe sandbox) y la Capa 5 (pantalla completa sin pestaña nueva) del modelo de seguridad del módulo. Reutilizado tal cual en tres lugares: previsualización en Gestión de Reportes, visor de usuario, y (potencialmente) cualquier futura vista que necesite renderizar HTML no confiable.


Decoradores

Decorador Nombre Tipo Descripción
@Input htmlEndurecido string (required) HTML ya procesado por ReporteSanitizerService.endurecerHtml(). Este componente nunca llama a endurecerHtml() por su cuenta — el contrato es que llega listo.
@Input mostrarBotonPropio boolean (default true) false cuando el contenedor ya trae su propio control de pantalla completa (evita duplicarlo — así lo usa VistaReporteComponent)

Propiedades clave

Propiedad Tipo Descripción
urlSegura SafeResourceUrl \| null Blob URL del HTML, envuelto con DomSanitizer.bypassSecurityTrustResourceUrl()
enPantallaCompleta boolean Sincronizado con el evento nativo fullscreenchange
cargando / error boolean / string \| null Estado de render del blob

Flujo principal

ngOnChanges(htmlEndurecido)
  -> renderizar()
     -> new Blob([htmlEndurecido], {type: 'text/html'})
     -> URL.createObjectURL(blob)
     -> sanitizer.bypassSecurityTrustResourceUrl(blobUrl)
     -> urlSegura = ...
ngOnDestroy()
  -> URL.revokeObjectURL(blobUrlActual)   // libera memoria, no deja blobs huérfanos

Métodos clave

alternarPantallaCompleta()

Fullscreen API nativa (Element.requestFullscreen() / document.exitFullscreen()) sobre el <div> contenedor — nunca sobre el <iframe> directamente, para poder mostrar el botón de salir superpuesto durante el fullscreen. Deliberadamente no usa window.open(): ver la decisión descartada en el overview.


Interfaz HTML del <iframe>

<iframe
  [src]="urlSegura"
  sandbox="allow-scripts allow-downloads allow-modals"
  referrerpolicy="no-referrer"
  ...
></iframe>

sandbox es texto estático, nunca [sandbox]="...". Angular arroja NG0910 en runtime si el atributo sandbox de un <iframe> con [src] dinámico está bindeado — es una protección deliberada del framework: con un binding, el valor podría variar y colar allow-same-origin en tiempo de ejecución, justo lo que este control impide. Si hay que cambiar los permisos concedidos, se edita el atributo directamente en la plantilla.

Tokens concedidos y por qué:

Token Por qué se concede
allow-scripts El reporte necesita ejecutar su JavaScript
allow-downloads Los reportes de ejemplo tienen KPIs con descarga de datos al clic
allow-modals Para alert()/confirm() dentro del reporte

Tokens nunca concedidos: allow-same-origin (el que anularía todo lo demás), allow-top-navigation*, allow-forms, allow-popups*, allow-pointer-lock, allow-presentation.


Observaciones

  • :host { display: block; width: 100%; height: 100%; } es obligatorio en el SCSS — sin él, el componente se comporta como elemento inline dentro de cualquier contenedor flex padre y se encoge al tamaño de su contenido en vez de llenar el espacio disponible (bug real detectado: el iframe caía al tamaño intrínseco por defecto del navegador, ~300×150px).
  • Cualquier contenedor flex que use este componente debe darle flex: 1; min-width: 0; min-height: 0; explícitamente — un flex item no crece por defecto sin flex-grow.

Changelog

Fecha Autor Cambio
2026-08-14 Iker Acevedo Vargas Versión inicial con sandbox de 3 tokens, blob URL, y Fullscreen API nativa reemplazando el botón de pestaña nueva