Iterate through IJSObjectReference in Blazor

Viewed 389

In a Blazor Component that uses JS interop, how can I iterate through a JavaScript array-like object?

Take the following example:

@inject IJSRuntime _js

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var elements = await _js.InvokeAsync<IJSObjectReference>("document.getElementsByClassName", "foo");
            // This loop does not compile, because IJSObjectReference is not IEnumerable:
            foreach (var x in elements)
            {
                [...]
            }
        }
    }
}

I tried using InvokeAsync<IEnumerable>(...), InvokeAsync<IJSObjectReference[]>(...) and InvokeAsync<IEnumerable<IJSObjectReference>>(...), but they throw exceptions:

Unhandled exception rendering component: "An exception occurred executing JS interop: The JSON value could not be converted to System.Collections.IEnumerable. Path: $ | LineNumber: 0 | BytePositionInLine: 1.. See InnerException for more details."

I tried casting the IJSObjectReference to IEnumerable, but it also throws an exception:

Unhandled exception rendering component: "Unable to cast object of type 'Microsoft.JSInterop.Implementation.JSObjectReference' to type 'System.Collections.IEnumerable'."

The documentation explains how to call JavaScript methods from Blazor and how to call methods on the returned JavaScript objects, but it does not mention iteration.

1 Answers

Try if this is of any help for you:

int count = await elements.InvokeAsync<int>("getProperty", "length");
for (int i=0; i<count; i++)
{
   IJSObjectReference element = await _js.InvokeAsync<IJSObjectReference>("at", i);
   //process element
}

You might need to define getProperty in your JavaScript file like this:

/* JS interop currently does not allow reading object properties.
   Hack from https://github.com/dotnet/aspnetcore/issues/25756#issuecomment-916314817 */
Object.defineProperty(Object.prototype, "getProperty", {
    value: function getProperty(key) {
        return this[key];
    },
    writable: true,
    configurable: true
});
Related