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 ;)

17 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!
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.

Invocando métodos estáticos escritos en C# desde Javascript

Para que un método estático escrito en C#, ya sea utilizando Blazor Server o Blazor WebAssembly, pueda ser accesible desde Javascript, éste debe estar decorado con el atributo [JSInvokable]. El método puede encontrarse en el interior de una página, en una clase personalizada o donde queramos; siempre que esté decorado de esa forma, Blazor podrá localizarlo.

El siguiente método podría valer como ejemplo:
public class Calculator
{
    [JSInvokable]
    public static int Sum(int a, int b)
    {
        return a + b;
    }
}
Para ejecutar este método desde Javascript, podríamos hacerlo de forma muy sencilla utilizando el método invokeMethodAsync() del objeto DotNet, un proxy proporcionado en el lado cliente por el framework:
var sum = await DotNet.invokeMethodAsync("BlazorJsDemo", "Sum", 1, 2);
alert(sum);
invokeMethodAsync() retorna un objeto Promise, por lo que puede esperarse su retorno utilizando await o bien mediante el clásico then():
DotNet.invokeMethodAsync("BlazorJsDemo", "Sum", 1, 2).then(sum => alert(sum));
En cualquier caso, el primer parámetro de invokeMethodAsync() indica el nombre del ensamblado donde se encuentra el método a invocar, que normalmente coincidirá con el nombre del proyecto.
Seguidamente hay que especificar el nombre del método, y luego los parámetros a enviarle.

Observad que esto podría crear problemas de ambigüedad si tenemos varios métodos estáticos invocables con el mismo nombre dentro del mismo ensamblado. En este caso, la llamada anterior fallará en tiempo de ejecución si existe un método Sum() invocable en cualquier otra clase del ensamblado BlazorJsDemo, mostrando por consola un error como el siguiente:
blazor.server.js:8 Uncaught (in promise) Error: System.InvalidOperationException: 
The assembly 'BlazorJsDemo' contains more than one [JSInvokable] method with identifier 'Sum'.
All [JSInvokable] methods within the same assembly must have different identifiers. 
You can pass a custom identifier as a parameter to the [JSInvokable] attribute.
Para solucionarlo, podemos emplear el parámetro identifier del atributo [JSInvokable] y así indicar un identificador distinto en uno de los métodos:
[JSInvokable("Add")]
public static int Sum(int a, int b)
{
    return a + b;
}
Los métodos pueden recibir y retornar cualquier tipo de datos, pues Blazor gestionará automáticamente la serialización y deserialización. Y también pueden ser asíncronos, como en el siguiente ejemplo:
public static async Task<ProcessResult> DoSomethingComplex()
{
    await Task.Delay(5000);  // Simulate a hard job
    return new ProcessResult() { ... };
}

¡Ojo al momento de ejecución!

Un aspecto sumamente importante a la hora de hacer invocaciones de este tipo es que tenemos que asegurar que el framework Blazor haya sido inicializado previamente en la página, pues de lo contrario encontraremos errores como el siguiente en la consola del navegador:
Blazor: No .NET call dispatcher has been set
Esto normalmente no ocurrirá porque Blazor es inicializado en cuanto carga la página, pero si quisiéramos hacer una invocación muy temprana podríamos utilizar un código como el siguiente en Blazor WebAssembly:
<!-- File: wwwroot/index.html -->
...
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script>
    Blazor.start({})
        .then(async () => {
            // Blazor was initialized
            var sum = await DotNet.invokeMethodAsync("BlazorJsDemo", "Sum", 1, 2);
            alert(sum);
        });
</script>
Y el código equivalente en Blazor Server:
...
@* File: Pages/_Host.cshtml *@
<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
    Blazor.start({})
        .then(async () => {
            // Blazor was initialized
            var sum = await DotNet.invokeMethodAsync("BlazorJsDemo", "Sum", 1, 2);
            alert(sum);
        });
</script>
En ambos casos, fijaos que lo que hacemos es cargar los scripts propios de Blazor pero estableciendo el atributo autostart a false para que el framework no arranque automáticamente.

Justo después, llamamos a Blazor.start() para arrancarlo manualmente, pero utilizamos then() para tomar el control en el momento en que finaliza su inicialización.

A partir de ese punto ya podremos considerar Blazor cargado, por lo que podremos invocar métodos estáticos sin problema.

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!