Search code examples
c#.netblazorblazor-server-sidemaui-blazor

Blazor Error: JSON Deserialization Issue with JS Interop


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


Solution

  • 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>