Blazor Error: JSON Deserialization Issue with JS Interop

192 Views Asked by At

Hello fellow developers,

I'm encountering an issue with my Blazor component that involves JavaScript interop. Specifically, when trying to get a reference to the table body in JavaScript and process its rows, I'm facing a JSON deserialization error. The error message suggests that the JSON value received from JavaScript cannot be converted to Microsoft.JSInterop.IJSObjectReference[]. I've included relevant portions of my Blazor component and JavaScript code.

Has anyone else experienced a similar problem or can offer insights into what might be causing this issue? I appreciate any help or guidance in resolving this JSON deserialization challenge. Thank you in advance for your expertise!

Please note: I'm working inside a Razor class library that is being referenced to a Blazor server app and MAUI Blazor App. The Javascript code is not referenced and it was only to test if I'm getting any values, which I am using the javascript code. I want to insert the data in a SQL Table but unable to do so.

<tbody id="table-body">
    <!-- Rows will be added here dynamically -->
    @for (int index = 0; index < Items.Count; index++)
    {
        var item = Items[index];
        <tr>
            <td>
                <div style="display: flex; align-items: center;">
                    <input type="checkbox" class="me-1" @bind="item.Selected" />
                    @(index + 1)
                    <button class="ms-2 btn btn-sm btn-danger" @onclick:preventDefault @onclick="() => DeleteRow(index)" style="display: @(item.Selected ? "block" : "none")">Delete</button>
                </div>
            </td>
            <td><input type="text" class="form-control" @bind="item.Name" /></td>
            <td><input type="text" class="form-control" @bind="item.Quantity" /></td>
            <td>
                <input type="text" list="uomOptions" class="form-control" @bind="item.UOM" />
                <datalist id="uomOptions">
                    <option value="Piece"></option>
                    <option value="Each"></option>
                    <option value="Box"></option>
                    <!-- Add more UOM options as needed -->
                </datalist>
            </td>
            <td>
                <input type="date" class="form-control" @bind="item.RequiredBy" min="@DateTime.Now.ToString("dd-MM-yyyy")" />
            </td>
        </tr>
    }
</tbody>
<div class="col-12">
    <button class="btn btn-primary rounded-pill" @onclick="AddRow">Add Row</button>
    <button class="btn btn-primary rounded-pill" @onclick="StoreAndCallCSharpMethod">Get Data</button>
</div>
<script defer>
    // JavaScript method to store values in an array and log to console
    function storeAndCallCSharpMethod() {
        var dataArray = [];

        // Loop through each dynamically added row
        var tableBody = document.getElementById('table-body');
        var rows = tableBody.getElementsByTagName('tr');

        for (var i = 0; i < rows.length; i++) {
            var row = rows[i];
            var inputs = row.getElementsByTagName('input');

            // Get values from input fields
            var name = inputs[1].value;
            var quantity = inputs[2].value;
            var uom = inputs[3].value;
            var requiredBy = inputs[4].value;

            // Store values in an object
            var rowData = {
                Name: name,
                Quantity: quantity,
                UOM: uom,
                RequiredBy: requiredBy
            };

            // Add the object to the array
            dataArray.push(rowData);
        }

        // Log the array to the console
        console.log(dataArray);
    }
</script>
@code {
    List<Item> dataArray = new List<Item>();

    public async Task GetAndLogTableData()
    {
        // Get table body from JS
        var tableBody = await JSRuntime.InvokeAsync<IJSObjectReference>("eval", "document.getElementById('table-body')");

        // Get all rows
        var rows = await tableBody.InvokeAsync<IJSObjectReference[]>("querySelectorAll", "tr");

        foreach (var row in rows)
        {
            // Get input fields within the current row
            var inputs = await row.InvokeAsync<IJSObjectReference[]>("querySelectorAll", "input");

            // Map to RowData object
            var rowData = new Item
                {
                    Name = await inputs[1].InvokeAsync<string>("value"),
                    Quantity = await inputs[2].InvokeAsync<string>("value"),
                    UOM = await inputs[3].InvokeAsync<string>("value"),
                    RequiredBy = await inputs[4].InvokeAsync<DateTime>("value") // Assuming RequiredBy is a string, adjust accordingly
                };

            // Add to list
            dataArray.Add(rowData);
        }

        foreach (var data in dataArray)
        {
            await insert.PRInsert(data.Name, data.Quantity, data.UOM, data.RequiredBy);
            Console.WriteLine($"{data.Name}, {data.Quantity}, {data.UOM}, {data.RequiredBy}");
        }
    }
}

Using the below method shows errors related to length Error: Microsoft.JSInterop.JSException: Could not find 'get' ('get' was undefined). I've tried different things but nothing works

private async Task StoreAndCallCSharpMethod()
{
    var tableBody = await JSRuntime.InvokeAsync<IJSObjectReference>("eval", "document.getElementById('table-body')");
    var rows = await tableBody.InvokeAsync<IJSObjectReference>("getElementsByTagName", "tr");

    for (int i = 0; i < await rows.InvokeAsync<int>("get", "length"); i++)
    {
        var row = await rows.InvokeAsync<IJSObjectReference>("get", i);
        var inputs = await row.InvokeAsync<IJSObjectReference>("getElementsByTagName", "input");

        var name = await inputs.InvokeAsync<string>("get", 1, "value");
        var quantity = await inputs.InvokeAsync<string>("get", 2, "value");
        var uom = await inputs.InvokeAsync<string>("get", 3, "value");
        var requiredBy = await inputs.InvokeAsync<string>("get", 4, "value");

        var rowData = new Item
            {
                Name = name,
                Quantity = quantity,
                UOM = uom,
                RequiredBy = DateTime.Parse(requiredBy)
            };

        dataArray.Add(rowData);
    }

    // Log the list to the console
    Console.WriteLine("Logging from C#:");
    foreach (var data in dataArray)
    {
        Console.WriteLine($"{data.Name}, {data.Quantity}, {data.UOM}, {data.RequiredBy}");
    }
}

Anyone insterested in all the kinds of errors I encountered may refer to the ChatGPT chat below Chat

1

There are 1 best solutions below

1
On BEST ANSWER

Based on your code, I could make it work on my side.

You can refer to the followng code:

async Task StoreAndCallCSharpMethod()
    {
       var dataArray = await JSRuntime.InvokeAsync<List<RowData>>("storeAndCallCSharpMethod");
        
 
        // Log the list to the console
        Console.WriteLine("Logging from C#:");
        // foreach (var data in dataArray)
        // {
        //     Console.WriteLine($"{data.Name}, {data.Quantity}, {data.UOM}, {data.RequiredBy}");
        // }
    }

And the script is as follows:

   <script src="_framework/blazor.server.js"></script>
<script defer >
            // JavaScript method to store values in an array and log to console
            function storeAndCallCSharpMethod() {
                var dataArray = [];
 
                // Loop through each dynamically added row
                var tableBody = document.getElementById('table-body');
                var rows = tableBody.getElementsByTagName('tr');
 
                for (var i = 0; i < rows.length; i++) {
                    var row = rows[i];
                    var inputs = row.getElementsByTagName('input');
 
                    // Get values from input fields
                    var name = inputs[1].value;
                    var quantity = inputs[2].value;
                    var uom = inputs[3].value;
                    var requiredBy = inputs[4].value;
 
                    // Store values in an object
                    var rowData = {
                        Name: name,
                        Quantity: quantity,
                        UOM: uom,
                        RequiredBy: requiredBy
                    };
 
                    // Add the object to the array
                    dataArray.push(rowData);
                }
                // Log the array to the console
                console.log(dataArray);
 
                return dataArray;
            }
</script>