Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can not receive promise in Blazor

I am doing a POST request from Blazor to a server via js interop.The post request gets to the server, is computed, is sent back. The js handler calls resolve for the promise, but the data does not get back to Blazor.

JS Interop

window.methods={
    submit: function () {
            return new Promise((resolve, reject) => {
                var form = document.getElementById("newTestForm");
                var data = new FormData(form);
                var xhr = new XMLHttpRequest();


                var method = form.getAttribute('method');
                var action = form.getAttribute('action');
                xhr.open(method, action);
                xhr.onload = function () {
                    if (xhr.status == 200) {
                        resolve(xhr.response); //data gets here in correct format !!!!!!!
                    }
                    else if (xhr.status != 200) {
                        reject("Failed to submit form with status" + xhr.status);
                    }
                }
                xhr.send(data);
            });
        }
}

Controller

[HttpPost]
[Route("/myroute")]
public async Task<string> ReturnData()
{
    await Task.Delay(1000);
    return "hello";
}

Blazor Component

<form method="post" action="/myroute">
....some inputs
</form>
<button onclick="@SubmitAsync"></button>
@functions{
      public static async Task<string> SubmitNewTestAsync()
        {
            try
            {
                var data = await JSRuntime.Current.InvokeAsync<string>("methods.submit");

                return data;
            }
            catch (Exception ex)
            {

                throw;
            }

        }

     public async Task SubmitAsync()
     {
         string data= await SubmitNewTestAsync();
     }
}

The response is correct, and the js method submit calls resolve on the result. I first thought it could be a deserialization problem. But it does not throw any error.

I have tried with different types of response ( bytes,objects,string) and there is still no response nor exception.

What could the problem be?

P.S I need to use the XMLHttpRequest since I want to stay on the same page. I should see exceptions if they exist in Blazor since I am testing using Server-Side hosting

Update

Ok so after trying many things the problem seems to be on the Blazor side specifically in the JSRuntime.Current.InvokeAsync<Type> method.
It seems the request will just finish without returning something (nor does it throw any error ) unless Type is primitive/object/dynamic. I do not now if this is a Blazor specific problem anymore.

Example

Considering the following model:

[Serializeable]
public class MyType{
 public int value{get;set;}
}

So this is what happens (Blazor side):

var data = await JSRuntime.Current.InvokeAsync<MyType>("methods.submit"); //fails
var data = await JSRuntime.Current.InvokeAsync<object>("methods.submit"); //works
var data = await JSRuntime.Current.InvokeAsync<dynamic>("methods.submit"); //works

So far tested also with long,int and string and they work. It seems there is no problem in receiving primitives.

like image 396
Bercovici Adrian Avatar asked Oct 30 '25 12:10

Bercovici Adrian


2 Answers

This might be the result of using the static JSRuntime.Current property which has been removed. Instead, you should inject the IJSRuntime into your component thus:

@inject IJSRuntime JSRuntime;

public static async Task<string> SubmitNewTestAsync()
    {
        try
        {
            var data = await JSRuntime.InvokeAsync<string>("methods.submit");

            return data;
        }
        catch (Exception ex)
        {

            throw;
        }

    }

Try this code snippet in your JavaScript function:

xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(xhr.responseText);
      } else {
         reject("Failed to submit form with status" + xhr.status);
      }
    };
like image 161
enet Avatar answered Nov 03 '25 07:11

enet


I had same issue with Stripe payment promise. Call .NET methods from JavaScript functions in ASP.NET Core Blazor looked like is good way to implement it. Then i found Blazor Javascript Interoperability blog post which solved problem briliantly.

Here are just some guidelines please fallow article to understand concept.

Javascript

function WithPromise(promiseHandler) {
   var somePromise =  promise.then((value) => {
   promiseHandler.invokeMethodAsync('SetResult', JSON.stringify(value));    
}

Promise handler

public class PromiseHandler : IPromiseHandler
{   
    public TaskCompletionSource<string> tcs { get; set; }

    [JSInvokable]
    public void SetResult(string json)
    {
        // Set the results in the TaskCompletionSource
        tcs.SetResult(json);
    }
}

Calling some function from c#

public Task<string> SomePromiseCall()
{
    var tcs = new TaskCompletionSource<string>();
    var promiseHandler = DotNetObjectReference.Create<PromiseHandler>(new PromiseHandler() { tcs = tcs });
    _jsRuntime.InvokeAsync<object>("WithPromise", promiseHandler);

    return tcs.Task;
}

like image 42
valentasm Avatar answered Nov 03 '25 07:11

valentasm