Me gustaría crear una biblioteca de componentes web usando elementos angulares que tengan plantillas predeterminadas pero que permitan a los desarrolladores anular la salida.
Por ejemplo, considere un componente de resultados de búsqueda. Podría tener una plantilla predeterminada que se vea así:
<h1>Search results for: {{query}}</h1>Pero un desarrollador podría querer cambiar la salida a esto (como un ejemplo arbitrario, debe ser flexible):
<h1>You searched for <strong>{{query}}</strong></h1> Mi primer pensamiento fue simplemente usar ng-content como este:
<search-results> <h1>You searched for <strong>{{query}}</strong></h1> </search-results> Sin embargo, esto no funciona y generará literalmente {{query}} .
Luego descubrí que podía agregar un parámetro de entrada TemplateRef en mi componente y pasar un elemento ng-template que analizaría las expresiones. Esto funciona bien dentro de una aplicación Angular, pero no estoy seguro de cómo hacerlo fuera de un contexto Angular usando los componentes web compilados que genera Angular Elements.
¿Alguien puede explicar cómo hacer esto?
Veo dos soluciones.
Solución 1: plantilla HTML
Defina unaplantilla HTML y pase su id al componente Angular. Allí clonas ese nodo (ver ejemplo en el enlace) y lo agregas al DOM.
Los marcadores de posición ( {{query}} ) no funcionan "fuera de la caja" en esa plantilla. Puede reemplazarlos manualmente o simplemente actualizar la plantilla y observar los cambios en el componente Angular. ( Observador de mutaciones )
Estoy trabajando en esta idea ahora mismo... Publicaré una actualización aquí una vez que mi código esté en GitHub para que puedas echarle un vistazo.
Solución 2: Plantillas JS
Otra idea es trabajar con plantillas JS. ( EJS por ejemplo)
Usted define una cadena de plantilla que pasa al componente Angular. Allí lo renderiza con el objeto de datos dado.
Puede crear una función que analice la .....{{variable}}... a ...value...
replaceText(content: string) { const match = content.match(/(\{\{\w+\}\})/g) match?.forEach(x => { const variable = x.slice(2).slice(0, -2) || "fool" const value = (this as any)[variable] || "" content = content.replace(x, value) }) return content }Luego, almacena el HTML interno de "ng-content" en ngAfterviewInit. Cuando lo necesite, llame a esta función.
Imagine algo como: vea que el "ng-content" está debajo de un div "inner" con display: none
@Component({ template: ` <div class="alert alert-{{ type }} alert-dismissible" *ngIf="show"> <div [innerHTML]="newContent"></div> <button type="button" class="close"> <span (click)="show = false; closed.emit()">×</span> </button> </div> <div #inner class="hidden"> <ng-content></ng-content> </div> `, styles:[` .hidden{ display:none } `] })En ngAfterVieInit
@ViewChild('inner', { static: false }) inner!: ElementRef; content:any; ngAfterViewInit(): void { this.content = this.inner.nativeElement.innerHTML; }y cuando lo necesites
this.newContent = this.satinizer.bypassSecurityTrustHtml( this.replaceText(this.content || '') );Ver un stackblitz simple
Utilice el método bypassSecurityTrustHtml de DomSanitizer , proporcionado por un angular; y vincúlelo con html <div [innerHtml]="getSearchText()"></div> .
public getSearchText() { return this.domSanitizer.bypassSecurityTrustHtml(`You searched for <b>${this.searchText}</b>`); }Para obtener más información, visite la documentación angular https://angular.io/api/platform-browser/DomSanitizer