¿Podemos representar un componente Blazor como un fragmento DOM independiente, o de alguna otra manera consumirlo como un componente web estándar dentro de una página HTML/JS estándar?
Esta podría ser una pregunta ingenua desde el punto de vista arquitectónico de Blazor. No soy un experto en Blazor ni mucho menos, pero creo que puede ser una técnica útil para la modernización incremental "brownfield" de aplicaciones web heredadas. Me sorprende que esto no parezca tener soporte oficial.
Para ilustrar, considere este ejemplo de componente web simple, que representa un elemento personalizado <date-info> :
// define a custom web component customElements.define("date-info", class DateInfo extends HTMLElement { constructor() { super(); // create an "open" (vs "closed") shadow DOM, // ie, accessible to the outside JavaScript this.attachShadow({ mode: "open" }); } async connectedCallback() { console.log(`${this.constructor.name}.${this.connectedCallback.name} called`); // get the content from the server, // this could be a Blazor component markup try { const response = await fetch("https://worldtimeapi.org/api/ip"); const data = await response.json(); const content = new Date(data.utc_datetime).toString(); this.shadowRoot.innerHTML = `<span>${content}</span>`; } catch(e) { console.error(e); const info = document.createTextNode(e.message); this.shadowRoot.appendChild(info); } } }); <!-- use the web component --> <p>Current time: <date-info/></p> Ahora, en lugar de obtener https://worldtimeapi.org/api/ip , me gustaría obtener y representar un marcado separado para un componente Blazor/Server, por ejemplo:
@* this is a Blazor component *@ <p>@(DateTime.Now)</p>Además, espero que este marcado siga siendo funcional y dinámico, es decir, los eventos DOM del lado del cliente y las actualizaciones del lado del servidor para este componente de Blazor se propaguen aún más en ambos sentidos, a lo largo del ciclo de vida del componente web envolvente.
Seguramente es posible convertirlo en una @page de @page y cargarlo en un iframe , pero prefiero representarlo como parte del DOM de la página exterior.
Hasta ahora, me he encontrado con esto:
Aparentemente, ese no era uno de los objetivos de diseño de Blazor en 2018: https://github.com/dotnet/aspnetcore/issues/16033 .
Más tarde, Steve Sanderson creó una biblioteca (no oficial) para probar los componentes de Blazor de forma aislada, que en teoría se puede usar para obtener un marcado de componente de Blazor independiente: https://stackoverflow.com/a/60457390/1768303 .
¿Es el mejor enfoque para abordar este problema, hasta ahora?
MS ha abordado esta limitación, pero la solución requiere .Net 6.
https://github.com/aspnet/AspLabs/tree/main/src/BlazorCustomElements
Esto fue hecho por el mismo hombre, Steve Sanderson.
No estoy seguro de si esto ayuda, pero definitivamente puede hacerlo desde una página del lado del servidor (borraré esta respuesta si no lo hace). Aquí hay una página de prueba que representa las tres "páginas" estándar dentro de una página cshtml con contenido del lado del servidor. En realidad, debe olvidar el concepto de "página" en Blazor. TODO es un Componente. Las páginas son solo componentes con un atributo personalizado de Page .
El problema con esta configuración es que tan pronto como actualiza la página, reinicia los tres componentes y pierde los datos del alcance.
@page "/test" @namespace StackOverflow.Answers.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ Layout = null; } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>StackOverflow.Answers</title> <base href="~/" /> <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" /> <link href="css/site.css" rel="stylesheet" /> <link href="StackOverflow.Answers.styles.css" rel="stylesheet" /> </head> <body> <div class="m-2 ps bg-light"> <h3>A Normal Razor Page</h3> <p> Lots of server side rendered junk </p> <component type="typeof(StackOverflow.Answers.Shared.SurveyPrompt)" render-mode="ServerPrerendered" /> </div> <div class="m-2 ps bg-info"> <h3>A Normal Header</h3> <p> Lots More server side rendered junk </p> <component type="typeof(StackOverflow.Answers.Pages.Counter)" render-mode="ServerPrerendered" /> </div> <div class="m-2 ps bg-light"> <h3>A Normal Header</h3> <p> Lots More server side rendered junk </p> <component type="typeof(StackOverflow.Answers.Pages.Counter)" render-mode="ServerPrerendered" /> </div> <div class="m-2 ps bg-light"> <h3>Yet Another Normal Header</h3> <p> Lots More server side rendered junk </p> <component type="typeof(StackOverflow.Answers.Pages.FetchData)" render-mode="ServerPrerendered" /> </div> <div class="m-2 ps bg-light"> <h3>Yet Another Normal Header</h3> <p> Lots More server side rendered junk </p> <component type="typeof(StackOverflow.Answers.Pages.FetchData)" render-mode="ServerPrerendered" /> </div> <div id="blazor-error-ui"> <environment include="Staging,Production"> An error has occurred. This application may no longer respond until reloaded. </environment> <environment include="Development"> An unhandled exception has occurred. See browser dev tools for details. </environment> <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.server.js"></script> </body> </html>Mientras tanto, puede mezclar el viejo cshtml con componentes de maquinilla de afeitar.
Utilizo este enfoque para mantener el mismo diseño gráfico entre los dos sistemas.
Un ejemplo, el siguiente archivo es _ Layout.cshtml utilizado por Identity .
He usado varios componentes de Blazor a través de renderizado estático:
@using Microsoft.AspNetCore.Hosting @using Microsoft.AspNetCore.Mvc.ViewEngines @inject IWebHostEnvironment Environment @inject ICompositeViewEngine Engine @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @using Project.Server.Shared <!DOCTYPE html> <html> <head> <component type="typeof(MainLayoutHead)" render-mode="Static" /> </head> <body> <app> <div class="container main"> <component type="typeof(MainLayoutTopImages)" render-mode="Static" /> <div class="row navmenu-row"> <div class="col-md-12 bg-dark navmenu-col"> <component type="typeof(NavMenu)" render-mode="Static" /> </div> </div> <div class="row content pt-4 pb-0 mt-0"> <div class="col-md-12"> <div class="row"> <div class="col-md-12"> @*Required for GDPR.*@ <partial name="_CookieConsentPartial" /> </div> </div> <div class="row body-row"> <div class="col-md-12 body-col"> @RenderBody() </div> </div> </div> </div> <component type="typeof(MainLayoutFooter)" render-mode="Static" /> </div> </app> <script src="~/Identity/lib/jquery/dist/jquery.min.js"></script> <script src="~/Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> <script src="~/Identity/js/site.js" asp-append-version="true"></script> @RenderSection("Scripts", required: false) </body> </html> MainLayoutHead , MainLayoutFooter y NavMenu son componentes regulares de Blazor.