2

I was using the async IJSRuntime to run JsInterrop function in my WASM project with a JS module to load the function.

    private readonly Lazy<Task<IJSObjectReference>> moduleTask;
    public LocalJsInterop(IJSRuntime jsRuntime)
    {
      moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
          "import", "./../_content/WebPlayerControls/localJsInterop.js").AsTask());
    }

and then using it like that to call a function

    public async ValueTask<string> GetWindowLocation()
    {
      var module = await moduleTask.Value;

      string value = await module.InvokeAsync<string>("GetWindowLocation");

      return value;
    }

I just saw recently that I can use IJSInProcessRuntime to run the function synchronously and tried to change my functions to use it.

But I can't get my module to work. I tried to init it like this

module = jsRuntime.Invoke<IJSObjectReference>("import", "./../_content/WebPlayerControls/localJsInterop.js");

But then it wouldn't give access to the sync Invoke<> but only the asnyc InvokeAsync<>

I could probably put the functions in the index.html to use them but I was wondering if I could still work with the module. Does someone know how to make the import module work in a synchronous way ?

EDIT

I tried using IJSInProcessObjectReference like this

    private readonly Lazy<IJSInProcessObjectReference> moduleTask;

    public LocalJsInterop(IJSInProcessRuntime jsRuntime)
    {
      moduleTask = new(() => jsRuntime.Invoke<IJSInProcessObjectReference>("import", "./../_content/WebPlayerControls/localJsInterop.js"));
      m_jsRuntime = jsRuntime;
    }

    public void ShowMessage()
    {
      var module = moduleTask.Value;

      module.InvokeVoid("ShowMessage");
    }

But now the code execution is stuck at the InvokeVoid line. And if I try to use the InvokeVoidAsync, it give a no function found exception

1 Answers1

0

There are a couple problems with your attempt at using the synchronous methods. There is a lot of good information on various ways to implement JS Interop here. However, it is certainly lacking some key information.

IJSRuntime

First off, you cannot inject IJSInProcessRuntime. Only the base interface is injectable (IJSRuntime). If you want to use the Runtime interface and not deal with ObjectReference then you would have to downcast the injected object. For example:

public LocalJsInterop(IJSRuntime jsRuntime)
{
    ((IJSInProcessRuntime)jsRuntime).InvokeVoid("displayAlert", "Hello, World!");
}

As you might already know, in order to invoke JS functions directly from IJSRuntime they have to be declared on the global Window interface. Continuing with the example above that would look like this:

window.displayAlert = function(message) {
    alert(message);
}

IJSObjectReference

Second, you cannot synchronously import IJSObjectReference, and unfortunately cannot inject it either. If you want to use the ObjectReference interface (which it looks like you do based on your question), then you will have to import the object reference asynchronously. For example:

private IJSRuntime _runtime;
public LocalJsInterop(IJSRuntime jsRuntime)
{
    _runtime = jsRuntime;
}

public async Task RunJS() {
    // Option 1: Using object reference asynchronously
    using (var jsObjRef = await _runtime.InvokeAsync<IJSObjectReference>("import", "<relative_path_to_js_module>"))
    {
        await jsObjRef.InvokeVoidAsync("displayAlert", "Hello, World!");
    }

    // Option 2: Using object reference synchronously
    using (var jsObjRef = await _runtime.InvokeAsync<IJSInProcessObjectReference>("import", "<relative_path_to_js_module>"))
    {
        jsObjRef.InvokeVoid("displayAlert", "Hello, World!");
    }
}

As you probably know, using the IJSObjectReference interface allows you to call isolated JS/ES modules so you don't have to declare everything as part of the global Window interface. For example:

export function displayAlert(message) {
    alert(message);
}

Important final note: Notice the using statements in the IJSObjectReference example. The interface implements IAsyncDisposable and should be disposed of when you're finished with it either by wrapping it in usings or if you are storing it in a class variable for multiple uses, then your class should inherit from IAsyncDisposable and implement the dispose method. For example:

public class JsInteropClass : IAsyncDisposable
{
    public IJSObjectReference JsObjectReference;
    // Or
    public IJSInProcessObjectReference JsObjectReference;

    // Other class things

    public async ValueTask DisposeAsync()
    {
        GC.SuppressFinalize(this);
        if (JsObjectReference != null) await JsObjectReference.DisposeAsync();
    }
}
Wes Thompson
  • 462
  • 1
  • 5
  • 21