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 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
Blazor permite crear componentes que son ignorantes respecto al modelo de hosting que están utilizando en tiempo de ejecución. Es decir, si lo diseñamos apropiadamente, un componente podría funcionar indistintamente en Blazor Server y Blazor WebAssembly, lo cual sería ideal desde el punto de vista de su reutilización.
Sin embargo, desde el interior de estos componentes, a veces necesitaremos distinguir cuándo están siendo ejecutados en Blazor Server y cuándo en Blazor WebAssembly. ¿Cómo podemos conseguir esto? Pues tenemos muchas maneras...
Durante la implementación de páginas o componentes Blazor en archivos .razor
, es relativamente frecuente encontrarse con casos en los que nos interesa reutilizar un bloque de código Razor en más de un punto.
Por ejemplo, observad el siguiente ejemplo:
<h1>Todo list</h1>
<h2>Pending tasks</h2>
<ul>
@foreach (TodoItem item in TodoItems.Where(i=>!i.IsDone).OrderBy(i=>i.Priority))
{
<li>@item.Task, owned by @item.Owner and created at @item.CreatedAt</li>
}
</ul>
<h2>Finished tasks</h2>
<ul>
@foreach (TodoItem item in TodoItems.Where(i=>i.IsDone).OrderBy(i=>i.DateFinished))
{
<li>@item.Task, owned by @item.Owner and created at @item.CreatedAt</li>
}
</ul>
En este componente, podemos ver claramente que estamos repitiendo los dos bloques de código encargados de mostrar los elementos de cada una de las listas, por lo que, si en el futuro quisiéramos cambiar la forma de mostrar un TodoItem
, tendríamos que modificar el interior de los dos bloques. Es frecuente en estos casos optar por crear un nuevo componente que se encargue de ello, por ejemplo, llamado TodoListItem
:
<li>@Item.Task, owned by @Item.Owner and created at @Item.CreatedAt</li>
@code {
[Parameter]
public TodoItem Item { get; set;}
}
De esta forma ya tendremos el código de renderización del TodoItem
centralizado y podremos simplificar el bloque anterior eliminando la duplicidad:
<h1>Todo list</h1>
<h2>Pending tasks</h2>
<ul>
@foreach (TodoItem item in TodoItems.Where(i=>!i.IsDone).OrderBy(i=>i.Priority))
{
<TodoListItem Item="item" />
}
</ul>
<h2>Finished tasks</h2>
<ul>
@foreach (TodoItem item in TodoItems.Where(i=>i.IsDone).OrderBy(i=>i.DateFinished))
{
<TodoListItem Item="item" />
}
</ul>
Aunque conceptualmente la solución que hemos implementado es correcta, introduce un problema en nuestra aplicación: por el mero hecho de querer evitar la duplicación de código, estamos introduciendo en la página un número indeterminado de componentes, lo cual podría afectar drásticamente a su rendimiento.
Por llevarlo al extremo, imaginad que esas listas tienen miles de elementos. En este caso, en nuestra página estaríamos introduciendo miles de componentes, con lo que esto implica:
- Deberían instanciarse miles de componentes (objetos).
- Deberían ejecutarse los eventos del ciclo de vida de cada componente al crearlos, inicializarlos, renderizarlos, etc.
- Mientras se encuentren en la página cada componente ocuparía memoria, ya sea en cliente (Blazor WebAssembly) o en servidor (Blazor Server).
Esto podría llegar incluso a hacer una página inutilizable, por lo que es importante disponer de otros métodos para crear y reutilizar bloques de código HTML sin necesidad de crear componentes. Esta es una de las utilidades de los render fragments.
En el mundo de las suites profesionales de componentes, seguro que todos habéis oído hablar de Syncfusion. Esta compañía lleva desde 2001 ofreciendo componentes y marcos de trabajo para todo tipo de entornos, con el fin de hacer la vida más sencilla a los desarrolladores: ASP.NET Core, ASP.NET MVC, Web Forms, Angular, React, Vue, Xamarin, Flutter, WinForms o WPF son sólo algunas tecnologías en las que tenemos a disposición sus soluciones.
Y claro, no podía faltar Blazor :) La biblioteca Syncfusion Blazor UI ofrece más de setenta componentes para Blazor Server y WebAssembly que cubren sobradamente las necesidades más habituales en la construcción de aplicaciones profesionales.
Aunque se trata de una solución comercial y las licencias tienen coste, la buena noticia es que existe una generosa licencia community gratuita, mediante la cual podemos tener acceso a todos los componentes siempre que seamos desarrolladores independientes o una empresa con menos de seis trabajadores e ingresemos anualmente menos 1 millón de dólares brutos.
En este post vamos a dar un primer vistazo a esta biblioteca, para tener una idea de su alcance y forma de uso.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: blazor, blazorserver, blazorwasm, componentes, syncfusion
Es frecuente que alumnos de mi curso de Blazor en CampusMVP me pregunten sobre la existencia de bibliotecas de componentes que les ayuden a desarrollar aplicaciones profesionales más rápidamente, por lo que no podía pasar por alto la noticia que publicaban hace unos días los amigos de Radzen en su cuenta de Twitter:
En efecto, ¡los componentes para Blazor de Radzen han pasado a ser open source y distribuidos bajo licencia MIT!
Para los que no los conozcan, Radzen es uno de los referentes en el mundo de las herramientas y componentes visuales para el desarrollo rápido de aplicaciones web, pero lo que nos ocupa ahora son el conjunto de más de 60 componentes visuales para Blazor Server y WebAssembly que ahora podremos utilizar de forma totalmente gratuita (bueno, aunque existen opciones para pagar por servicios de soporte profesional).
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: blazor, blazorserver, blazorwasm, componentes
El parser de Blazor de versiones anteriores a la 5.0 era bastante permisivo con los espacios no significativos que los desarrolladores dejamos a la hora de escribir nuestro código de marcado. Es decir, todos los espacios incluidos en el código fuente, fueran importantes o no para el resultado final, formaban parte del proceso de renderización y, por tanto, trasladados tal cual al navegador.
Entendemos por espacio no significativo aquél carácter espacio, tabulador, saltos de línea o similares que el navegador no va a representar visualmente al componer la página. Por ejemplo, un marcado como "<h2>1 2</h2>
" suele representarse en el browser exactamente igual que si hubiéramos enviado "<h2>1 2</h2>
" (salvo que hayamos usado reglas CSS específicas en dirección contraria), porque todos los espacios intermedios son ignorados.
En Blazor, es habitual implementar la interfaz de nuestros componentes de forma declarativa, mezclando etiquetas que:
- Pueden ser HTML, y cuyo resultado irá textualmente al navegador
- Pueden ser instancias de otros componentes, por lo que al navegador irá el resultado de la renderización de éstos.
Por ejemplo, el siguiente código envía al navegador la etiqueta <h1>
y luego el resultado de renderizar el componente Blazor <MyComponent>
, en cuyo interior podrán existir a su vez otras etiquetas o componentes:
@page "/home"
<h1>Welcome!<h1>
<MyComponent />
Fijaos que, aunque es la fórmula que emplearemos en la mayoría de ocasiones, es una estructura totalmente rígida: podemos tener la total certeza de que el componente que será instanciado en la página anterior es MyComponent
, pero, ¿qué ocurre si queremos poder decidir en tiempo de ejecución qué componente instanciaremos en la página?
Hace unos días fue lanzada la nueva versión de ASP.NET Core basada en el flamante .NET 5, que incluye un buen número de novedades para los desarrolladores Blazor, algunas de ellas bastante jugosas.
En este post vamos a ver por encima las que creo que son más destacables en esta nueva entrega:
- .NET 5 y mejoras de rendimiento
- CSS Isolation
- JavaScript Isolation
- Virtualización
- InputRadio & InputRadioGroup
- Soporte para IAsyncDisposable
- Soporte para upload de archivos
- Control del foco
- Parámetros de ruta catch-all
- Protected browser storage
- Prerenderización WebAssembly
- Lazy loading de áreas de aplicación en Web Assembly
- Otros cambios y mejoras
Publicado por José M. Aguilar a las 10:13 a. m.
Etiquetas: blazor, blazorserver, blazorwasm, novedades
Un alumno del curso de Blazor que tutorizo en CampusMVP me exponía problema bastante curioso con el que se había topado al implementar una página que, simplificando el escenario, venía a ser como la siguiente:
@page "/sum/{a:decimal}/{b:decimal}"
<h1>The sum is: @(A+B)</h1>
@code {
[Parameter] public decimal A { get; set; }
[Parameter] public decimal B { get; set; }
}
Esta página funcionaba correctamente con números enteros: podíamos acceder a "/sum/1/2" y veíamos el resultado correcto ("The sum is: 3"). Esto era así tanto accediendo a través del sistema de routing de Blazor (es decir, usando un link hacia esa ruta desde dentro de la aplicación) como accediendo directamente a la página introduciendo la ruta en la barra de direcciones del navegador.
Sin embargo, el problema surgía al usar valores decimales. Si desde dentro de la aplicación pulsábamos un enlace hacia "/sum/1.1/2.2", el resultado obtenido era correcto ("The sum is: 3.3"). Sin embargo, si en ese momento refrescábamos esa página, o bien accedíamos directamente a ella introduciendo la URL en el navegador, el servidor retornaba un error 404.
Extraño, ¿no?
Hace unos días, un alumno de mi curso de Blazor en CampusMVP me preguntaba sobre la posibilidad de detectar cuándo un componente se estaba procesando en "modo prerenderización", con el fin de ejecutar cierta lógica únicamente en ese caso.
Me habría gustado responderle algo como "usa la propiedad IsPrerendering
del componente y ya lo tienes". Pero no, las cosas no son tan sencillas; Blazor no proporciona ninguna propiedad del estilo, y no existen fórmulas directas para conseguirlas, más allá de un par de hacks bastante poco elegantes:
- Intentar acceder al contexto HTTP, sólo disponible cuando estamos ejecutando código en el interior de una petición (es decir, cuando el componente se está prerenderizando).
- Ejecutar una función Javascript mediante interop, y capturar la excepción que se produce al hacerlo mientras se está prerenderizando el resultado, pues durante este proceso no es posible utilizar los mecanismos de interoperación.
En este post vamos a ver una alternativa más apropiada que las anteriores, sacando partido al inyector de dependencias y al hecho de que la prerenderización se ejecuta en el propio proceso de la petición que carga la página inicialmente. Y como veréis, la idea es muy sencilla.
Todos los usuarios que están utilizando una aplicación Blazor Server mantienen una conexión abierta con el servidor para enviar eventos y recibir las modificaciones de la interfaz de usuario. Y si habéis trabajado algo con Blazor, sabréis que cuando dicha conexión se corta aparece en el navegador un mensaje como el mostrado en la siguiente captura de pantalla:
Como se puede ver en la captura anterior, cuando se detecta la desconexión, se añade automáticamente a la página un <div>
con el identificador components-reconnect-modal
que bloquea la página (observad su posición fija a pantalla completa), mostrando al usuario un mensaje informándole de que se está intentando reconectar.
Si, transcurridos algo más de 30 segundos, la reconexión no ha sido posible, el contenido del <div>
cambiará para informar al usuario de que la conexión no pudo ser restablecida, y ofreciendo un botón para reintentarlo y un enlace para recargar la página completa:
Podemos comprobar muy fácilmente estos comportamientos si lanzamos la aplicación sin depuración (Ctrl+F5) desde Visual Studio y detenemos IIS Express desde su icono en la barra de herramientas de Windows.
Como comportamiento por defecto la verdad es que no está nada mal, pues nos proporciona una solución out of the box que será suficiente la mayoría de los casos. Sin embargo, la estética es obviamente mejorable... ¿y si quisiéramos modificar visualmente estos mensajes o incluso su comportamiento? Pues es lo que veremos en este post 🙂
<NumberList Count="10" />
Desde el punto de vista del componente <NumberList>
, la recepción de este parámetro es trivial, simplemente indicando el atributo [Parameter]
en su propiedad pública Count
:<ul>
@for (int i = 0; i < Count; i++)
{
<li>@i</li>
}
</ul>
@code {
[Parameter]
public int Count { get; set; }
}
Este mecanismo va bien si queremos pasar el parámetro a un componente específico mediante atributos, pero puede haber ocasiones en las que nos interesa pasar parámetros a todos los componentes que se encuentren por debajo en la estructura jerárquica sin tener que ir pasándolos de uno en uno. Esto puede ser especialmente útil si, como en el diagrama, existen varios niveles de componentes en los que deseamos tener acceso a la misma información.¡Bienvenidos a los cascading values and parameters, o valores y parámetros en cascada!