Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta blazor. Mostrar todas las entradas
Mostrando entradas con la etiqueta blazor. Mostrar todas las entradas
martes, 2 de febrero de 2021
Blazor

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.

martes, 15 de diciembre de 2020
Blazor

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?

martes, 1 de diciembre de 2020
Blazor

Una pequeña pero interesante novedad introducida en Blazor 5, es la posibilidad de escribir nuestras propias factorías de componentes, o, como les llaman en el framework, Component Activators.

El Component Activator es un objeto singleton que implementa la interfaz IComponentActivator, cuyo único requisito es el cumplimiento del siguiente contrato:

public interface IComponentActivator
{
    IComponent CreateInstance(Type componentType);
}

Como se puede intuir, Blazor invocará al activator registrado en el sistema justo en el momento en que va a instanciar un componente: se le suministra el tipo como parámetro, y retornará ya un objeto IComponent, la interfaz más básica que cumplen todos los componentes Blazor.

Por defecto, se utiliza como activador la clase interna DefaultComponentActivator:

internal class DefaultComponentActivator : IComponentActivator
{
    public static IComponentActivator Instance { get; } = new DefaultComponentActivator();

    public IComponent CreateInstance(Type componentType)
    {
        var instance = Activator.CreateInstance(componentType);
        if (!(instance is IComponent component))
        {
            throw new ArgumentException($"The type {componentType.FullName}" 
              + $"does not implement {nameof(IComponent)}.", nameof(componentType));
        }

        return component;
    }
}

Obviamente, si quisiéramos intervenir en el proceso de construcción de componentes, sólo tendremos que implementar nuestra clase personalizada y registrarla como singleton en el inyector de dependencias, asociada a la interfaz IComponentActivator.

Para crear nuestra propia implementación, fijaos que la clase DefaultComponentActivator es interna y no podemos heredar de ella, pero tiene un código tan simple que podemos usarlo para crear nuestras propias versiones.

Veamos un par de ejemplos.

martes, 24 de noviembre de 2020
Blazor

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:

  1. .NET 5 y mejoras de rendimiento
  2. CSS Isolation
  3. JavaScript Isolation
  4. Virtualización
  5. InputRadio & InputRadioGroup
  6. Soporte para IAsyncDisposable
  7. Soporte para upload de archivos
  8. Control del foco
  9. Parámetros de ruta catch-all
  10. Protected browser storage
  11. Prerenderización WebAssembly
  12. Lazy loading de áreas de aplicación en Web Assembly
  13. Otros cambios y mejoras
¡Vamos allá!
martes, 17 de noviembre de 2020
Blazor

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?

martes, 27 de octubre de 2020
Blazor

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.

martes, 20 de octubre de 2020

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:

Mensaje modal indicando que se está intentando reconectar con el servidor

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:

Mensaje indicando que la reconexión no ha podido ser restablecida

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 🙂

martes, 6 de octubre de 2020
Blazor Los parámetros son el mecanismo básico de comunicación entre componentes, pues permiten que un componente padre o contenedor puede especificar, utilizando atributos, los valores que desea enviar de forma individualizada a los componentes utilizados en su interior. Un ejemplo lo vemos en el siguiente bloque de código, donde indicamos a un componente el número de veces que debe mostrar un contenido:
<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; }
}
Estructura jerárquica de componentes 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!
martes, 29 de septiembre de 2020
Blazor

Como sabemos, al acceder por primera vez a una aplicación Blazor WebAssembly, durante unos segundos aparecerá en el navegador el mensaje "Loading...", indicando que se están descargando los recursos necesarios para que funcione la aplicación.

Vimos también en su momento que este mensaje puede ser modificado a nuestro antojo para sustituirlo por algo más apropiado, como un espectacular spinner:

Mensaje de carga personalizado

Sin embargo, si el navegador desde el que estamos accediendo es un vetusto Internet Explorer u otro sin soporte para Blazor WebAssembly, el mensaje de carga se mostrará indefinidamente en el navegador. No se mostrarán errores, ni siquiera por la consola, por lo que el usuario se quedará esperando sin saber que, en realidad, la aplicación no cargará jamás.

Lo que vamos a ver en este post es cómo detectar este escenario e informar al usuario de que para acceder a la aplicación debe usar un browser más moderno.

martes, 22 de septiembre de 2020
Blazor Hoy vamos a hablar de templated components, una interesante capacidad que ofrece Blazor para crear componentes genéricos, capaces de operar con tipos de datos arbitrarios definidos durante la instanciación del componente.

Pero para entender su utilidad, creo que lo mejor es comenzar por un ejemplo de componente simple como el siguiente, que se encarga de mostrar una colección de objetos Friend formateados de una manera determinada:
@* File: ItemList.razor *@
@{
    var count = 1;
    foreach (var item in Items)
    {
        <div class="counter">Item #@(count++)</div>
        <div class="item">
            @ChildContent(item)
        </div>
    }
}

@code
{
    [Parameter]
    public IEnumerable<Friend> Items { get; set; }
    [Parameter]
    public RenderFragment<Friend> ChildContent { get; set; }
}
Fijaos que estamos utilizando un RenderFragment tipado. Si no sabes muy bien lo que es esto, puedes echarle un vistazo al post Componentes con cuerpo en Blazor.
Como hemos podido comprobar, lo único que hace el componente <ItemList> es recorrer la colección de amigos disponible en la propiedad Items y, por cada elemento, mostrar un contador que va incrementándose en cada iteración, renderizando el RenderFragment<Friend> con el ítem actual.

<ItemList> podría ser utilizado desde cualquier otro componente, por ejemplo, de la siguiente manera:
<h1>My friends</h1>

<ItemList Items="@Friends">
    <p>@context.Name is @context.Age years old</p>
</ItemList>

@code {
    public Friend[] Friends { get; set; }

    protected override void OnInitialized()
    {
        Friends = new[] {
            new Friend() {Name = "John", Age = 32}, 
            new Friend() {Name = "Peter", Age = 23}, 
            new Friend() {Name = "Ally", Age = 31}
        };
    }
}
Si ejecutamos un código como el anterior, podremos que ver todo es correcto: la lista de amigos se muestra correctamente. Bueno, todo es correcto, excepto cuando nos preguntamos si este mismo componente podría servir para otros tipos de objeto distintos a Friend.

Esto lo hemos vivido ya antes, muchos años atrás, cuando aún no existían los tipos genéricos en C# y .NET. Porque lo que realmente nos interesaría para lograr una mayor reutilización de código es que el componente <ItemList> fuera genérico y pudiera aplicarse a cualquier tipo de datos.
martes, 15 de septiembre de 2020
Blazor

¡Os traigo buenas noticias! Me complace anunciaros que por fin está disponible en el catálogo de CampusMVP el curso en el que he estado trabajando intensamente durante varios meses, y que me consta que muchos estabais esperando: Desarrollo de aplicaciones Web con Blazor.

Su creación ha sido bastante laboriosa porque queríamos ofreceros el mejor y más completo curso sobre Blazor que podéis encontrar en este momento, y no es fácil conseguirlo cuando se trata de una tecnología tan reciente, de la que aún no existe tanta documentación, bibliografía y ejemplos como las hay de otras tecnologías con más años de recorrido. Ha sido duro, pero tanto un servidor como el equipo de producción de CampusMVP que ha participado en su creación, estamos orgullosos del resultado y firmemente convencidos de que lo que hemos logrado: un recorrido práctico, minucioso y profundo del que es, sin duda es el framework que cambiará vuestra forma de desarrollar aplicaciones para la web.

En este post vamos a intentar resolver las siguientes cuestiones:

martes, 7 de julio de 2020
Blazor En esta serie sobre interoperación Javascript-Blazor hemos ido viendo cómo desde Blazor Server o WebAssembly podíamos llamar a funciones Javascript disponibles en el browser, y también cómo conseguir invocar métodos estáticos .NET desde Javascript.

Como recordaréis del post anterior, la invocación de métodos estáticos era bastante sencilla, porque básicamente desde Javascript sólo teníamos que conocer el nombre del ensamblado donde se encontraba el código y el nombre del método a ejecutar. El hecho de que el método fuera estático es una ventaja, pues no hay "piezas móviles" en el puzzle.

Sin embargo, si desde Javascript queremos invocar un método de instancia, la cosa se complica un poco porque tendremos que ayudar a Blazor a determinar de qué instancia se trata, y esto a priori no suena sencillo porque Javascript y Blazor viven en dos mundos diferentes (de hecho, en el caso de Blazor Server incluso están físicamente separados). Sin embargo, veremos que las herramientas que ofrece el framework son suficientes para llevarlo a cabo sin liarnos demasiado.

Esta última entrega la dedicaremos precisamente a esto: aprender cómo podemos llamar desde Javascript a métodos de instancia escritos en C#.
martes, 23 de junio de 2020
Blazor
Hace poco veíamos mecanismos de interop que permitían invocar código Javascript desde Blazor, con objeto de poder seguir utilizando código existente en bibliotecas o componentes, ya sean nuestros o de terceros.

Pero Blazor también aporta mecanismos para la interoperación en sentido contrario, es decir, permitir que desde código Javascript podamos invocar métodos escritos en C#, ya sea con Blazor Server o Blazor WebAssembly.

De hecho, Blazor permite invocar métodos tanto estáticos como de instancia, aunque en este post nos centraremos sólo en los primeros porque son más sencillos de entender. En un artículo posterior profundizaremos en el segundo escenario.
martes, 16 de junio de 2020
Blazor Aunque uno de los objetivos de partida de Blazor es permitir que toda la lógica de presentación de una aplicación pueda ser implementada sin tocar una línea de Javascript, sería absurdo ignorar que este lenguaje dispone de uno de los ecosistemas más amplios y ricos del mundo del software. Sin duda, sería una pena (y un error) intentar ignorar la ingente cantidad de bibliotecas y componentes que existen para él.

Tampoco podemos olvidar que puede que nosotros mismos tengamos bibliotecas Javascript que nos podría interesar reutilizar en nuevas interfaces de usuario. De nuevo, sería un error que el hecho de saltar a Blazor nos obligara a reescribir todo desde cero.

Por estas razones Blazor dispone de mecanismos para interoperar con Javascript bidireccionalmente, pues:
  • desde nuestras aplicaciones Blazor podemos invocar funciones Javascript,
  • y desde Javascript podemos invocar métodos escritos en C# con Blazor.
En este post vamos a centrarnos en la primera posibilidad, dejando la segunda para otras publicaciones de la serie que llegarán más adelante.
martes, 9 de junio de 2020
Blazor En este post vamos a echar un vistazo a algo muy básico, pero que seguro que ayuda a los que estáis comenzando con Blazor para haceros una mejor idea su funcionamiento: cómo mostrar u ocultar elementos en la página.

La idea sería conseguir que, en el siguiente componente, la pulsación del botón etiquetado como "Toggle" haga que el bloque <div> donde se encuentra el "Hello world!" se oculte y se muestre sucesivamente:
<h1>Visibility demo</h1>
<div>
    <h2>Hello world!</h2>
</div>

<button class="btn btn-primary" @onclick="Toggle">Toggle</button>

@code {
    private bool _isVisible = true;

    private void Toggle()
    {
        _isVisible = !_isVisible;
    }
}
Como es de esperar, al ejecutar este código veremos que cuando el elemento está visible aparece tanto en la página como en los elementos de la misma (es decir, en el DOM):

El elemento aparece en la página y en el DOM

Como en otros marcos de trabajo para la construcción de webs SPA, con Blazor podemos utilizar distintas técnicas para mostrar u ocultar elementos:
  • Añadiendo o eliminando físicamente el elemento del DOM.
  • Utilizando estilos en línea que modifiquen su visibilidad.
  • Aplicando clases CSS que modifiquen su visibilidad.
martes, 2 de junio de 2020
Blazor La verdad es que el mensaje "Loading..." que aparece en el navegador mientras la aplicación Blazor WebAssembly está cargando es bastante insulso: un fondo blanco, con el texto sin estilo alguno. Es cierto que aparece sólo unos segundos, y a veces mucho menos que eso, pero si queremos que nuestra aplicación tenga un aspecto profesional, deberíamos hacer algo por mejorarlo.

Veamos cómo.
martes, 19 de mayo de 2020
Blazor Normalmente, en demos y ejemplos de Blazor solemos ver el uso componentes sin cuerpo, muchas veces escritos en forma de tags autocerrados, como los siguientes:
<Article Title="A new hope" DatePublished="@DateTime.Now"></Article>
<Article Title="A new hope" DatePublished="@DateTime.Now" />
El código de este componente, en Article.razor, podría ser el siguiente:
<h1>@Title</h1>
<p>Post content goes here...</p>
@code {
    [Parameter]
    public string Title { get; set; }
    [Parameter]
    public DateTime DatePublished { get; set; }
}
En este punto, es lógico pensar que nuestro artículo tendrá su texto, por lo que deberíamos poder incluirle un cuerpo. Una posibilidad sería incluir en el componente una propiedad Body y enviarle el cuerpo en el momento de su instanciación, como en el ejemplo que vemos a continuación:
<Article Title="A new hope" DatePublished="@DateTime.Now" Body="This is the post content..."></Article>
Pero, como podréis imaginar, esto es bastante limitado. Aparte de la legibilidad nula que tendría a nivel de código si el texto es medianamente extenso, no podríamos utilizar marcado HTML ni otros componentes Blazor en su interior, por lo que no es una solución práctica. Lo que en realidad nos interesa es poder hacer lo siguiente:
<Article Title="A new hope" DatePublished="@DateTime.Now">
    <p>This is the post content</p>
    <p>Blah, blah, blah...</p>
</Article>
Sin embargo, si probáis a ejecutar el código anterior obtendréis un error parecido al siguiente:
InvalidOperationException: Object of type 'MyBlazorProject.Pages.Article' does not have a property matching the name 'ChildContent'.
El mensaje de error no nos indica por qué ocurre, aunque apunta en la dirección para poder solucionarlo. Como no podría ser de otra forma, Blazor permite la creación de componentes con cuerpo.
martes, 12 de mayo de 2020
Blazor En Blazor Server, cada usuario que esté utilizando la aplicación establece una conexión SignalR para enviar al servidor sus interacciones con la página, así como para retornar de vuelta al usuario las modificaciones del DOM realizadas desde el servidor. Esta conexión, junto con la información del estado del cliente almacenada en el servidor, es lo que en Blazor Server se denomina un "circuito".

Y ya que el servidor es consciente en todo momento de los usuarios conectados, en teoría debería ser posible contarlos para, por ejemplo, mostrarlos en pantalla como en la siguiente captura:

Aplicación Blazor mostrando el número de usuarios conectados

En este post vamos a ver cómo conseguir precisamente eso: incluir en las páginas del proyecto un contador de usuarios conectados que irá actualizándose en tiempo real, para, por el camino, profundizar un poco en el ciclo de vida de los circuitos de Blazor Server.
martes, 28 de abril de 2020
Blazor Hoy va un truquillo rápido sobre Blazor que puede resultar útil en muchos escenarios. Como probablemente sepáis, al igual que ocurre con otras tecnologías, Blazor codifica la salida HTML por motivos de seguridad, de forma que cualquier contenido con código de marcado será mostrado por defecto tal cual, sin interpretar las etiquetas.

Por ejemplo, considerad el siguiente código en un componente Blazor:
<p>This text is encoded: @myHtml</p>

@code {
    string myHtml = "Hello, <b>this is bold</b>";
}
El resultado que enviaremos al navegador es el siguiente:
<p>This text is encoded: Hello, &lt;b&gt;this is bold&lt;/b&gt;</p>
Y, por tanto, nuestros usuarios podrán leer literalmente este párrafo:
This text is encoded: Hello, <b>this is bold</b>
Normalmente no es eso lo que queremos mostrarles, ¿verdad?
martes, 21 de abril de 2020
Blazor Es lógico pensar que muchas de las aplicaciones que construyamos con Blazor, sobre todo en el mundo empresarial, necesitarán acceder a bases de datos para obtener o almacenar información.

En caso de utilizar Blazor WebAssembly, ese acceso encajará conceptualmente con lo que venimos haciendo desde hace años al construir aplicaciones SPA: desde el lado cliente simplemente llamaremos a una API o endpoint que actuará como pasarela, por lo que todo el código de acceso a datos se encontrará en el servidor. Aunque existen algunas iniciativas para posibilitar que EF Core funcione directamente en el navegador con algunos proveedores, ciertamente no será el escenario habitual.

Sin embargo, si utilizamos Blazor Server, tendremos la sensación inicial de que es todo más sencillo. El código de eventos bindeados a la interfaz se ejecuta en servidor, por lo que a priori sólo tendríamos que replicar lo que solemos hacer con servicios o controladores: solicitar una instancia del contexto de datos al inyector de dependencias, y utilizarla para lograr nuestros propósitos.

Pero no, la cosa es algo más complicada que eso. De hecho, los que ya estéis creando aplicaciones Blazor Server con EF Core seguro que os habéis topado en algún momento con un error como el siguiente:
InvalidOperationException: A second operation started on this context before a previous operation 
completed. This is usually caused by different threads using the same instance of DbContext. For 
more information on how to avoid threading issues with DbContext, see 
https://go.microsoft.com/fwlink/?linkid=2097913.
Esta excepción se debe a que existen dos hilos de ejecución utilizando la misma instancia del contexto de datos al mismo tiempo, algo que Entity Framework no permite. Esto ocurrirá, por ejemplo, si tenéis en la misma página varios componentes que de forma concurrente intentan acceder a la base de datos al inicializarse.

¿Por qué ocurre esto?