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.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: blazor, blazorinterop, blazorserver, blazorwasm
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.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: blazor, blazorinterop, blazorserver, blazorwasm
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):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.
Veamos cómo.
<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.
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:
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.
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, <b>this is bold</b></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?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?
@code
en el mismo archivo .razor
, como en el siguiente bloque de código:@* File: ~/Pages/HelloWorld.razor *@
@page "/hello"
<h1>Hello</h1>
<div>
<label for="name">Your name:</label>
<input id="name" @bind-value="Name" @bind-value:event="oninput"/>
</div>
@if (HasName)
{
<h2>Hello, @Name!</h2>
}
@code
{
public string Name { get; set; }
public bool HasName => !string.IsNullOrWhiteSpace(Name);
}
Sin embargo, esto no tiene que ser necesariamente así. En tiempo de compilación, un archivo .razor
genera una clase parcial C# con su mismo nombre, lo cual nos brinda la posibilidad de mover todo el código C# a otra porción de dicha clase.Podéis ver las clases generadas para cada archivo.razor
en la carpeta del proyectoobj\debug\netcoreapp3.1\Razor\Pages
.
En aquellos tiempos era aún poco más que un prototipo y no se podía saber si aquella línea de investigación llevaría a alguna parte, pero era tan prometedor que, algún tiempo después, a primeros de 2018, Blazor pasó a ser un proyecto experimental del equipo ASP.NET de Microsoft.
A partir de ese momento se fueron despejando las incógnitas que quedaban en cuanto a la viabilidad real de convertir aquello en un producto y se continuaron implementando funcionalidades, hasta que, en abril de 2019, Daniel Roth anunció que daban por concluida la fase experimental y Blazor entraba oficialmente a formar parte del stack de tecnologías para la web.
A día de hoy, Blazor es un framework completo para el desarrollo de aplicaciones web SPA, como lo pueden ser Angular, React o Vue. Dispone de sistema de bindings, routing, componentes, ciclo de vida, validaciones, plantillas, gestión de errores, inyección de dependencias, etc. Todo lo que podemos necesitar para crear aplicaciones web profesionales de calidad.
La primera versión de Blazor se lanzó acompañando a .NET Core 3.0 en Septiembre de 2019, aunque sólo en uno de sus modelos de hospedado: el denominado server-side o Blazor Server.
Pero no era ese el único plan...