Blazor ha venido en ASP.NET Core 8 cargadito de novedades, aunque probablemente la más destacable sea la introducción de las Blazor Web Apps como modelo de proyecto que unifica los distintos modos de renderizado de componentes. Aunque se trata de un cambio positivo, la realidad es que ha complicado algunas cosas que antes, con unos modelos de proyecto más sencillos, eran más fáciles de implementar.
Un ejemplo claro lo tenemos en las páginas de error 404 (not found): con este nuevo modelo unificado no hay una fórmula trivial o integrada de serie en el framework para implementar esta funcionalidad, tan habitual en nuestras aplicaciones.
En este post vamos a ver un posible enfoque para conseguir que si un usuario introduce una ruta inexistente en el navegador o bien pulsa un enlace interno que no exista, podamos mostrarle una página de error 404 totalmente personalizada, implementada como componente Blazor, e integrada en nuestra Blazor Web App.
Este post pertenece a una serie de tres partes donde estamos viendo cómo renderizar componentes Blazor en el interior de vistas MVC de ASP.NET Core. Hasta ahora, hemos visto cómo renderizar desde vistas MVC componentes Blazor usando los siguientes modos de renderización:
En esta entrega final veremos cómo renderizar componentes Blazor ejecutados por completo en el lado cliente (WebAssembly).
Este post pertenece a una serie de tres partes donde estamos viendo cómo renderizar componentes Blazor en vistas MVC de ASP.NET Core.
En la primera parte de la serie vimos cómo renderizar componentes estáticos (SSR) en servidor, y ahora vamos a centrarnos en hacerlo con componentes con interactividad también en el lado servidor (Blazor Server), dejando para una siguiente entrega los componentes interactivos ejecutados por completo en cliente con WebAssembly.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: aspnetcoremvc, blazor, blazorserver, trucos
Hace no demasiado, mientras analizábamos la posibilidad de que Blazor acabara en algún momento sustituyendo a MVC como tecnología "por defecto" para el desarrollo de aplicaciones web en .NET, comentaba que técnicamente no hay nada que impida a ambas tecnologías convivir pacíficamente en una misma aplicación. De hecho, están diseñadas para trabajar juntas :)
En este sentido, uno de los escenarios soportados es la inserción de componentes Blazor en el interior de vistas de una aplicación ASP.NET Core MVC. Esto puede ser muy interesante, entre otros casos, si queremos ir introduciendo Blazor progresivamente en aplicaciones MVC existentes o para reutilizar componentes entre distintos proyectos.
En esta miniserie vamos a ver cómo conseguirlo con los distintos modos de renderizado de Blazor, porque cada uno tiene sus particularidades:
- Renderizado estático (SSR), lo que veremos en este post.
- Renderizado en servidor (Blazor Server), en un futuro post.
- Renderizado en cliente (Blazor WebAssembly), también en un artículo posterior.
El espacio de nombres System.IO
de .NET proporciona clases para trabajar con archivos y directorios, algo que, a priori, no tendría demasiado sentido en componentes Blazor WebAssembly, ya que éstos se ejecutan en el navegador del cliente y, por motivos de seguridad, no tienen acceso al sistema de archivos del servidor, ni tampoco a los archivos del dispositivo del usuario que está ejecutando la aplicación.
El primero de los casos es salvable: si desde un componente Blazor WebAssembly quisiéramos leer o escribir archivos en el servidor, podríamos hacerlo a través de una API que, ejecutada en el servidor, tuviera acceso a este tipo de recursos.
Sin embargo, técnicamente no hay forma de acceder a los archivos locales del usuario, dado que los componentes Blazor WebAssembly se ejecutan en el sandbox del navegador, que impide acceder a los recursos del sistema.
Pues bien, a pesar de ello, y de forma totalmente contraria a lo que podríamos intuir, desde Blazor WebAssembly sí que se pueden usar los componentes de System.IO
(bueno, o muchos de ellos) para leer o escribir archivos y directorios, aunque con algunas peculiaridades que veremos a continuación.
Este tema lo he debatido con colegas en varias ocasiones y creo que abordarlo aquí puede resultar interesante para algunos más que os estéis preguntando lo mismo a raíz de la tracción que, poco a poco, va tomando Blazor entre los desarrolladores web que trabajamos con el stack de tecnologías de Microsoft.
Voy a explicar mi punto de vista al respecto contemplando dos aspectos distintos, que creo que son los fundamentales para poder responder apropiadamente a esta pregunta:
- Si técnicamente sería posible reemplazar MVC por Blazor, esto es, si Blazor dispone de mecanismos suficientes para cubrir las necesidades de una aplicación que tradicionalmente se desarrollaría con MVC.
- Si la popularidad y difusión de Blazor podría llegar a alcanzar las cotas suficientes para convertirse en una alternativa completa y segura a MVC, en términos de madurez, estabilidad, soporte y tamaño de la comunidad. O en otras palabras, si Blazor como producto llegará a estar lo suficientemente consolidado como para poder reemplazar a MVC.
Vamos a analizar cada una de ellas.
Disclaimer: obviamente, todo lo que veréis a continuación son opiniones personales y, como tales, pueden ser discutibles. Si tenéis una opinión diferente, estaré encantado de leerla en los comentarios.
Como sabéis, con ASP.NET Core 8 se ha incluido la posibilidad de que una página Blazor sea capaz de procesar directamente una petición HTTP, renderizando sus componentes y retornando el resultado HTML completo al lado cliente de forma estática. Es ya lo vimos hace algún tiempo cuando hablamos de Server-Side Rendering (SSR), una de las piezas clave para conseguir que Blazor sea un framework fullstack.
En la práctica, esta opción posibilita la implementación de sitios estáticos completos, como los que construiríamos con ASP.NET Core MVC, Razor Pages o cualquier otro tipo de tecnología de servidor, pero con la gran ventaja de que en este caso estaremos beneficiándonos del fantástico modelo de componentes Blazor, que es una gozada en términos de productividad y facilidad de uso, y sin perder la posibilidad de activar la interactividad de los componentes (su capacidad para reaccionar ante eventos del usuario), una vez que el HTML ha sido descargado en el navegador.
En aquél momento vimos que SSR no requiere una programación específica: los mismos componentes interactivos que luego podemos ejecutar en el lado cliente o servidor pueden ser renderizados mediante SSR de forma estática en el backend. Sin embargo, en algunos momentos nos podría resultar interesante saber si un componente está funcionando en modo estático (SSR) o interactivo (los tradicionales Blazor WebAssembly o Blazor Server).
Los que llevamos muchos años programando con ASP.NET/ASP.NET Core y todos los frameworks que han ido surgiendo en su ecosistema, estamos familiarizados con el concepto de "contexto HTTP".
Materializado en forma de objeto de tipo HttpContext
, el contexto HTTP es una de las piezas fundamentales de la infraestructura de ASP.NET Core, y nos permite acceder a información sobre la petición HTTP que se está procesando, como los encabezados, el cuerpo de la petición, las cookies, etc., así como base para la generación de la propia respuesta a través de su propiedad Response
. En muchos escenarios, se trata de un recurso imprescindible para procesar la petición de la forma adecuada, por lo que estamos acostumbrados a usarlo cuando es conveniente.
Sin embargo, cuando saltamos a Blazor, pronto nos llama la atención que HttpContext
no está disponible. Y si lo pensamos, esto tiene bastante sentido en los dos modos de renderización clásicos:
-
En Blazor WebAssembly, dado que el código .NET se ejecuta directamente en el cliente, no existen peticiones que procesar y, por tanto, no existe
HttpContext
. Se trata de una abstracción que sólo existe en el backend. -
En Blazor Server, aunque el código se está ejecutando en el servidor, tampoco tenemos disponible
HttpContext
porque realmente no existen peticiones: el lado cliente y servidor se comunican mediante un canal websockets implementado con SignalR, que es por donde viajan ascendentemente las acciones realizadas por el usuario y descendentemente las actualizaciones del DOM de la página.
Con la llegada de Blazor 8, ha tomado relevancia el nuevo modo de renderizado, llamado Server-Side Rendering (SSR) o renderización en el lado servidor, que ya vimos por aquí hace algún tiempo.
Como ya sabemos, el funcionamiento de Blazor SSR es similar al de otros frameworks de backend puros, como MVC o Razor Pages: el servidor recibe la petición, la procesa y genera una respuesta HTML que se envía al cliente. En este escenario, durante el proceso del componente Blazor sí existe un contexto HTTP.
En Blazor es posible acceder a valores de parámetros de la query string exclusivamente desde componentes de tipo página, es decir, aquellos definidos con la directiva @page
.
Para ello, bastaba con declarar una propiedad pública y decorarla con los atributos [Parameter]
y [SupplyParameterFromQuery]
. Por ejemplo, si desde una página quisiésemos obtener el valor del parámetro term
de la query string, podríamos hacerlo de la siguiente forma:
@page "/search"
<p>Searching term: @Term</p>
@code {
[Parameter]
[SupplyParameterFromQuery]
public string Term { get; set; }
}
Sin embargo, como sabéis, esto no funcionaba si intentábamos acceder así a estos parámetros desde componentes que no fueran páginas, es decir, que no fueran instanciados por el sistema de routing.
Como seguramente sabréis, Blazor es la gran apuesta de Microsoft para el desarrollo de aplicaciones web con .NET. Sólo hay que ver la cantidad de novedades que han introducido en la última versión para darse cuenta de que están poniendo toda la carne en el asador y, de alguna forma, está convirtiéndose en la opción preferida para el desarrollo de este tipo de aplicaciones.
Pues bien, es un placer anunciaros que, tras varios meses de preparación, hace unos días hemos puesto en marcha el nuevo curso de desarrollo de aplicaciones Web con Blazor en .NET 8, como siempre, en CampusMVP.
Ha sido un trabajo duro, porque esta última versión ha venida cargada de novedades (sobre todo en lo relativo al nuevo modelo unificado propuesto por las Blazor Web Apps) y hemos tenido que revisar en profundidad y reescribir parte del contenido del curso, rehacer ejemplos y regrabar material audiovisual, todo con el objetivo de seguir siendo el mejor y más completo curso de Blazor del mercado.
En este post voy a intentar responder a las siguientes preguntas:
- ¿Qué es Blazor?
- ¿Por qué es interesante aprender a desarrollar con Blazor?
- Si ya hemos trabajado con ASP.NET Core y MVC, ¿siguen valiendo estos conocimientos?
- ¿En qué consiste el curso de desarrollo con Blazor?
- ¿Cuáles son los contenidos del curso?
- ¿Qué conocimientos previos se necesitan para seguir el curso?
- Me convence, ¿cuándo empezamos?
Como sabemos, gran parte de las validaciones en formularios Blazor las implementamos usando anotaciones de datos, o data annotations. Estas anotaciones nos permiten validar los datos de entrada antes de que ejecutemos la lógica de negocio, que normalmente se encontrará en el handler del evento OnValidSubmit
del formulario.
Vemos un ejemplo de formulario Blazor aquí, y el código C# justo debajo:
@page "/friend"
<h3>FriendEditor</h3>
<EditForm Model="Friend" OnValidSubmit="Save">
<DataAnnotationsValidator />
<div class="form-group mb-2">
<label for="name">Name:</label>
<ValidationMessage For="()=>Friend.Name" />
<InputText @bind-Value="Friend.Name" class="form-control" id="name" />
</div>
<div class="form-group mb-2">
<label for="score">Score:</label>
<ValidationMessage For="()=>Friend.Score" />
<InputNumber @bind-Value="Friend.Score" class="form-control" id="score" />
</div>
<div class="form-group mb-2">
<label for="birthDate">BirthDate:</label>
<ValidationMessage For="()=>Friend.BirthDate" />
<InputDate @bind-Value="Friend.BirthDate" class="form-control" id="birthDate" />
</div>
<div class="form-group mb-2">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</EditForm>
@code {
private FriendViewModel Friend = new();
private async Task Save()
{
await Task.Delay(1000);
Friend = new FriendViewModel();
}
public class FriendViewModel
{
[Required(ErrorMessage = "El nombre es obligatorio")]
public string Name { get; set; }
[Range(0, 10, ErrorMessage = "La puntuación debe estar entre {1} y {2}")]
public int Score { get; set; }
public DateTime BirthDate { get; set; }
}
}
Como sabéis, hasta ahora, los componentes Blazor podían ejecutarse en dos tipos de hosting distintos, Blazor Server y Blazor WebAssembly. Aunque cada uno tiene sus escenarios ideales de uso, ambos enfoques conseguían lo mismo, implementar Single Page Applications en C#, sin necesidad de utilizar JavaScript (bueno, o al menos, minimizando radicalmente su uso):
- Con Blazor Server, se mantiene en el servidor una copia del DOM de cada usuario conectado, de forma que C# puede manipularlo directamente. Luego, mediante una conexión websockets, se sincronizan los cambios con el cliente.
- Blazor WebAssembly lleva al navegador el runtime de .NET, las bibliotecas base y los ensamblados de la aplicación, por lo que todo se ejecuta directamente en el lado cliente gracias a WebAssembly.
Estas dos formas de ejecutar componentes son muy apropiadas cuando se trata de elementos interactivos, capaces de responder a eventos de usuarios y con capacidad de actualizar el contenido de la página. Sin embargo, son muy poco eficientes cuando se trata de páginas estáticas que no requieren interacción con el usuario:
-
Para poder mostrar en el navegador un contenido, en Blazor Server hay que esperar a que el lado cliente descargue la página contenedora, un archivo JavaScript y se establezca la conexión websockets con el servidor, tras lo cual se enviará el contenido de la página.
-
En Blazor WebAssembly, el cliente debe descargar la página contenedora, los ensamblados y todo el entorno de ejecución de .NET y lanzarlo todo sobre WebAssembly. En este momento se puede mostrar el contenido de la página.
Hace poco, un alumno de mi curso de Blazor en CampusMVP me preguntaba si, para seguir con las viejas costumbres, desde Blazor Server era posible escribir en la consola del navegador.
Aunque no tiene demasiado sentido hacerlo dado que disponemos de buenas herramientas de depuración, me pareció un ejemplo interesante para aplicar los mecanismos de interoperación de Blazor con JavaScript. Además, quizás interese a alguien más, así que vamos a ver por aquí cómo hacerlo.
Pero antes de meternos en faena, vale la pena decir que lo que veremos no es específico de Blazor Server, sino que también podremos usarlo en Blazor WebAssembly. Los mecanismos básicos de interoperación con JavaScript son idénticos, por lo que en este post veremos cómo implementar un código válido para ambos tipos de hosting.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: blazor, blazorinterop, blazorserver, blazorwasm
Si habéis trabajado algo con Blazor, seguramente sabréis que los componentes escritos en archivos .razor
son traducidos en tiempo de compilación a lenguaje C# y posteriormente compilados como cualquier otra clase de nuestro proyecto.
Por tanto, ¿que impediría que nos saltásemos ese paso y escribiéramos nuestros componentes directamente en C#? Efectivamente, nada ;)
En este post veremos cómo hacerlo.
Pero antes, un disclaimer: apenas existen razones prácticas para implementar componentes visuales usando C#. Usando Razor todo será más sencillo, rápido y tendremos menos riesgo a equivocarnos, así que esa debe ser la opción por defecto. Por tanto, lo que vamos a ver aquí no debéis usarlo salvo en casos justificados (por ejemplo, cuando queráis crear componentes sin UI).
Hoy va un post cortito, pero que puede venir bien a alguien que esté intentando "trocear" su aplicación en distintos proyectos y se haya encontrado con este problema.
Echemos un vistazo al contenido típico del archivo App.razor
de una aplicación Blazor (Server o WebAssembly, da lo mismo):
<Router AppAssembly="@typeof(Program).Assembly">
...
</Router>
Resulta que el componente Router
escanea durante su inicialización el ensamblado que hayamos indicado en su atributo AppAssembly
, por defecto el ensamblado actual, en busca de componentes que:
- Hayan definido sus rutas mediante directivas
@page
de Razor, - o bien, que tengan su ruta especificada con atributos
[Route]
En cualquiera de los casos, los componentes localizados son añadidos a la tabla de rutas y, por tanto, será posible navegar hacia ellos modificando la ruta del navegador, ya sea programáticamente (usando el servicio NavigationManager
) o bien mediante la pulsación de enlaces o modificación directa en la barra de direcciones.
Hasta aquí, todo correcto. El problema viene cuando las páginas que queremos añadir a la tabla de rutas no están en el ensamblado actual, o se encuentran repartidas en varios ensamblados distintos, por ejemplo en:
- Otros proyectos de la solución
- Ensamblados externos, referenciados directamente
- Paquetes NuGet instalados en el proyecto
Si habéis utilizado isolation con módulos JavaScript en Blazor 5, probablemente lo que vamos a ver en este post os resulte muy interesante para organizar los archivos JavaScript de la aplicación, especialmente aquellos que sean utilizados desde un único componente.
Como recordaréis, el aislamiento de JavaScript de Blazor permitía cargar de forma dinámica un archivo .js
y ejecutar código definido en él. Por ejemplo, imaginad un módulo como el siguiente, definido en wwwroot/modules/MyPrompt.js
:
export function showPrompt(message, defaultValue) {
return prompt(message, defaultValue);
}
Hasta Blazor 6, daba la impresión de que la query string era un ciudadano de segunda clase en el sistema de routing, pues no se ofrecían mecanismos sencillos para la obtención de parámetros suministrados a través de esta vía, ni tampoco para la generación de URI que los utilizaran. De hecho, estas tareas teníamos que solucionarlas manualmente, aunque fuera con ayuda de paquetes externos como Microsoft.AspNetCore.WebUtilities
.
Ya con la llegada de esta versión, Blazor incorpora de serie herramientas que nos harán esto más llevadero, como su integración con el binding o, lo que veremos en este post, la posibilidad de generar direcciones que incluyan este tipo de parámetros.
Semanas atrás echamos un vistazo por encima a Blazor UI Components, los componentes profesionales para Blazor de Syncfusion, y algún tiempo después revisamos más en detalle uno de los grandes protagonistas en el mundo de los componentes, los de creación de gráficas estadísticas o Charts.
Sin embargo, si hay un tipo de componentes que todos usamos de forma frecuente en nuestras aplicaciones profesionales son, sin duda alguna, los grids o rejillas de datos. Por esa razón, los componentes que de alguna forma simplifican su creación son probablemente los más populares en todas las suites profesionales o gratuitas, sea cual sea la tecnología en la que trabajemos.
En este post centraremos el foco en Blazor DataGrid, la propuesta de Syncfusion para crear interfaces de rejillas de datos de forma sencilla y eficaz para Blazor Server y WebAssembly.
Pero antes de empezar, recordad que, aunque se trata de un producto comercial, dispone de una licencia community bastante permisiva, que permite que en muchos casos podamos utilizar los componentes de forma totalmente gratuita.
Nota: lo que estás leyendo es un post patrocinado por Syncfusion, pero en ningún momento han revisado previamente o condicionado de alguna forma su contenido.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: blazor, blazorserver, blazorwasm, sponsored, syncfusion
Como sabéis, a no ser que se especifique lo contrario mediante la directiva @inherits
, los componentes Blazor heredan de Microsoft.AspNetCore.Components.ComponentBase
. Esta clase abstracta proporcionada por el framework hace que podamos centrarnos en escribir nuestro código, al encargarse de gestionar aspectos básicos de los componentes como su creación, inicialización, renderizado, el ciclo de vida u otros aspectos de infraestructura.
Pero aunque heredar de ComponentBase
es la inmensa mayoría de las veces la mejor opción, no tiene por qué ser así necesariamente. Blazor reconoce como componente cualquier clase que implemente IComponent
, una pieza mucho más pequeña.
Hace algunas semanas echamos un vistazo rápido a Blazor UI Components, la suite de componentes profesionales para Blazor proporcionada por Syncfusion.
Como vimos, se trata de una completa colección que soluciona las necesidades más habituales durante la creación de la interfaz de usuario de aplicaciones Blazor, tanto Server como WebAssembly. Más de 70 componentes que incluyen controles de entrada de datos, botones, calendarios, layouts, rejillas de datos o gráficas estadísticas, entre otros. Y lo mejor es que, aunque se trata de un producto comercial, su generosa licencia community permite que muchos podamos utilizarlos de forma totalmente gratuita.
Hoy vamos a profundizar un poco en los componentes de presentación de datos estadísticos, o Chart Components, que por su potencia, versatilidad y calidad del resultado merece un post en sí mismos.
Nota: lo que estáis leyendo es un post patrocinado por Syncfusion, pero en ningún momento han revisado previamente o condicionado de alguna forma su contenido.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: blazor, blazorserver, blazorwasm, componentes, sponsored, syncfusion