Return a list of JSObjectReference in Blazor using JS Interop

106 Views Asked by At

Is it possible to return a list of object references from JS to C#? Currently I do return a list of objects (THREE.js objects) though I am unable to deserialize it into a C# IList or array to get separate IJSObjectReference instances on .NET side. I simply get one IJSObjectReference (logically possible) which is a reference to the JS array itself.

Anyone know if it is possible to deserialize a JS Array into a C# array/list/collection and in this case, for it to contain JSObject references?

This is basically what I am trying to do

const myJsFunc () = () => {
    ....
    return [someJsObject, anotherJsObject]
}

C# side

var objectReferencesArray = js.Invoke<IJSInProcessObjectReference[]>("myJsFunc", ...);

or

var objectReferencesArray = js.Invoke<IList<IJSInProcessObjectReference>>("myJsFunc", ...);

neither work. I am only able to get ONE object ref which is for the array itself.

EDIT: Still interested in hearing suggestions on this but as an update I can say that I chose to simply return the list as its own IJSInProcessObjectReference and then when I pass it back to JS, destruct it and handle it that way.

2

There are 2 best solutions below

1
Suryateja KONDLA On

you cannot directly return a list of IJSObjectReference instead you can do like this

js Code

const myJsFunc = () => {
    return [JSON.stringify(someJsObject), JSON.stringify(anotherJsObject)];
}

c# Code

var jsonStringArray = js.Invoke<string[]>("myJsFunc", ...);
var objectReferencesList = new List<IJSObjectReference>();

foreach (var jsonString in jsonStringArray)
{
    var objectRef = await js.InvokeAsync<IJSObjectReference>("JSON.parse", jsonString);
    objectReferencesList.Add(objectRef);
}

this way u can get a list from JS

0
LoneSpawn On

Updated 2024-04-04

I now have 2 ways. The original answer, now answer 2, used a library I wrote. The new answer, answer 1, does not require any additional libraries and only takes a few minor changes.

Answer 1

  • In Javascript, wrap each object in the array using DotNet.createJSObjectReference() which returns a simple object. Ex. { __jsObjectId: 1 }

  • Send the array of __jsObjectId values to Blazor as the return value of your method as a long[]

  • In Blazor, you can then create an IJSInProcessObjectReference[] using the long[] and a little reflection.

Code

Javascript side:

const myJsFunc = () => {
    // create a object reference for each and return an array of the ids
    return [window, window, null].map(o => o === null || o === void 0 ? -1 : DotNet.createJSObjectReference(o).__jsObjectId);
}

C# side:

// get the class Type used for IJSInProcessObjectReference
Type WebAssemblyJSObjectReferenceType = typeof(Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime).Assembly.GetType("Microsoft.JSInterop.WebAssembly.WebAssemblyJSObjectReference")!;
// convert the object ids into array of IJSInProcessObjectReference
IJSInProcessObjectReference[]? objectReferencesArray = js.Invoke<long[]?>("myJsFunc")?.Select(id => 
 id < 0 ? null : (IJSInProcessObjectReference)Activator.CreateInstance(WebAssemblyJSObjectReferenceType, js, id)!).ToArray();

This is a bare min work up. You should cache the WebAssemblyJSObjectReference type by setting it to a static variable.

Answer 2

Using Nuget package SpawnDev.BlazorJS:
Program.cs

// Inject and init BlazorJSRuntime
builder.Services.AddBlazorJSRuntime();

Razor page (for example)

[Inject]
BlazorJSRuntime JS { get; set; }

...

var objectReferencesArray = JS.Call<IJSInProcessObjectReference[]>("myJsFunc", arg1, arg2, ...);

GitHub Repo: SpawnDev.BlazorJS

Documentation

SpawnDev.BlazorJS is a full Javascript interop library that contains over 300 Javascript types allowing complete access to the Javascript environment in Blazor WASM.

I am the author of SpawnDev.BlazorJS.
NuGet