dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.5k stars 10.04k forks source link

Blazor - Await JavaScript Promises #20899

Closed LukeTOBrien closed 4 years ago

LukeTOBrien commented 4 years ago

Hi there,

I'm a big fan of Blazor, I think it's a real game-changer, the best thing to enter the ASP world since MVC.

Anywho, I have a situation where I would like to show some basic message boxes, ie. error message, prompt, confirmation etc.
I decided to uses Bootbox.

This issue was that I needed to await upon the user clicking the OK button.
One solution to this would be to return a Promise that gets resolved when the user clicks OK.
However the problem is that when you await the JSRuntime.InvokeAsync<T> it expects the return type an actual value, you cannot return a Promise.

My solution was to use callbacks based on this post.
Here we are awaiting the result from a TaskCompletionSource that is set from a callback in JavaScript that is calling DotNet.InvokeAsync.

My suggestion is, can you manage to find a way to await Promises natively in the Blazor Framework C# code?
Something like:

var result = await JSRuntime.InvokePromiseAsync<T>()

What do ya think?

LukeTOBrien commented 4 years ago

So just to give background.
Our existing app is an olde WinForms app, and I was experimenting with porting our code that uses WinForms MessageBox to Blazor/JS.
So now in my solution I am calling the MessageBox in C#:

var result = await JsRuntime.ExecutePromiseAsync<DialogResult>("MessageBox", new MessageBoxArgs()
{
    Buttons = MessageBoxButtons.YesNo,
    Title = "Message",
    Text = "Are you okay?"
});

And in my JS (TypeScript) I have:

window['MessageBox'] = MessageBox;
...
export class MessageBox {
    constructor(callbackId: string, args: MessageBoxArgs) {
        console.log(args);
        let options: BootboxDialogOptions = {
            title: args.title,
            message: args.text
        };
        if (MessageBoxButtonStratergies[args.buttons]) {
            options.buttons = MessageBoxButtonStratergies[args.buttons](callbackId);
        }

        bootbox.dialog(options);
    }
}
...
var MessageBoxButtonStratergies = {};
MessageBoxButtonStratergies[MessageBoxButtons.YesNo] = function(callbackId: string) {
    return {
        yes: {
            label: 'Yes',
            class: 'btn-primary',
            callback: function() {
                DotNet.invokeMethodAsync(
                    "Framework",
                    "PromiseCallback",
                    callbackId,
                    "" + DialogResult.Yes);
            }
        },
        no: {
            label: 'No',
            class: 'btn-secondary',
            callback: function() {
                DotNet.invokeMethodAsync(
                    "Framework",
                    "PromiseCallback",
                    callbackId,
                    "" + DialogResult.No);
            }
        }
    };
}

export default MessageBoxButtonStratergies;
javiercn commented 4 years ago

@LukeTOBrien thanks for contacting us.

I'm having some failure understanding what you are trying to do. InvokeAsync will unwrap any given promise and return you back the result. How is that different from what you are trying to accomplish?

Are you trying, somehow, to pass a promise from JS to .NET as a method argument? Is that what you are asking for?

LukeTOBrien commented 4 years ago

hmm, okay well I feel a little embarrassed, it seems that Promises are already awaited as you say.
Initially the app was hanging, I must of been doing it wrong on my side and assumed that Promises were not supported.