Automatically refresh a component in Blazor

Dwayne Barsotta picture Dwayne Barsotta · Jun 20, 2019 · Viewed 9.4k times · Source

I am working on a Blazor page for adding a new object. The object "RepairOrder" has List of object "RepairSection".

On the page there is an area that will loop through the List "RepairOrder"."RepairSections" and show the elements:

   <div class="col-lg-10">
        @if (sections.Count > 0)
        {
            foreach (var sec in sections)
            {
                <h3>Section @sec.SectionNumber</h3>

                <div class="row">
                    <div class="col-lg-1"></div>
                    <div class="col-lg-5">
                        <label for="Failure" class="control-label">Failure: </label>
                        <input for="Failure" class="form-control" bind="@sec.Failure" readonly />
                    </div>
                    <div class="col-lg-1"></div>

                    <div class="col-lg-1">
                        <label><input type="checkbox" checked="@IsCApprovedChecked(sec)" />   Warranty</label>
                    </div>
                    <div class="col-lg-2">
                        <label><input type="checkbox" value="@IsWarrantyChecked(sec)" />   Repair Approved</label>
                    </div>
                </div>

                <div class="row">
                    <div class="col-lg-1"></div>
                    <div class="col-lg-10">
                        <label for="AdminComments" class="control-label">Admin Comments: </label>
                        <input for="AdminComments" class="form-control" bind="@sec.AdminComments" readonly />
                    </div>
                </div>
                <div class="row">
                    <div class="col-lg-1"></div>
                    <div class="col-lg-10">
                        <label for="TechComments" class="control-label">Tech Comments: </label>
                        <input for="TechComments" class="form-control" bind="@sec.TechComments" readonly />
                    </div>
                </div>
            }
        }
    </div>

After all the current sections in the list have been added to the page, there is a button to add a new section. When the button is clicked, the function changes a bool value to true to open a modal as a dialog. The modal contains fields to input a new section elements.

function called to open the modal:

protected void AddSectionCalled()
{
    _newSection = new RepairSection();
    this.isAddNew = true;
}

Modal Code:

<div class="modal" tabindex="-1" style="display:block" role="dialog">
        <div class="modal-dialog modal-dialog-scrollable modal-xl">
            <div class="modal-content">
                <div class="modal-header">
                    <h3 class="modal-title">New Repair Section</h3>
                    <button type="button" class="close" onclick="@CloseModal"><span aria-hidden="true">X</span></button>
                </div>
                <div class="modal-body">
                    <form>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-2">
                                <label for="sectionLetter" class="control-label">Section: </label>
                                <input for="sectionLetter" class="form-control" bind="@_newSection.SectionNumber" />
                            </div>
                            <div class="col-lg-1"></div>

                            <div class="col-lg-2">
                                <label><input type="checkbox" bind="@_newSection.Warranty" />   Warranty</label>
                            </div>
                            <div class="col-lg-2">
                                <label><input type="checkbox" bind="@_newSection.CustomerApproved" />   Repair Approved</label>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="_failure" class="control-label">Failure: </label>
                                <input for="_failure" class="form-control" bind="@_newSection.Failure"/>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="_adminComments" class="control-label">Admin Comments: </label>
                                <input for="_adminComments" class="form-control" bind="@_newSection.AdminComments" />
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="_techComments" class="control-label">Tech Comments: </label>
                                <input for="_techComments" class="form-control" bind="@_newSection.TechComments"/>
                            </div>
                        </div>
                        <br/>
                        <button class="btn btn-primary float-left" onclick="AddNewSection">Add New Section</button>
                    </form>
                </div>
            </div>
        </div>
    </div>

When the "Add New Section" button is clicked, the "_newSection" object created from the information collected in the modal is added to the "sections" list that was originally looped through when the page was loaded.

   private void AddNewSection()
        {
            sections.Add(_newSection);
            this.StateHasChanged();            
            this.isAddNew = false;

        }

as you can see I added the "StateHasChanged()" after the new section is added to the sections list. However the page does not render to display the new section.

I originally had created dummy data on the page "OnInitAsync()" event that loaded the sections list with data before it was displayed. This way I could make sure the page displayed what was in the list correctly.

How can I make the page re-render the information in the list after user adds a new object to the list?

----Edit-----

Below is the code for the entire page. I will try and minimize this on the weekend, however there really is not a lot here.

 @page "/AddRepairOrder"
@using ShopLiveWebVersion2._0.Models
@using ShopLiveWebVersion2._0.DataAccess
@using ShopLiveWebVersion2._0.Services
@using ShopLiveWebVersion2._0.Data
@inject IUriHelper UriHelper
@inject RepairOrderContext context

<div class="row">
    <div class="col-lg-1"></div>
    <div class="col-lg-10"><h1>Create New Repair Order</h1></div>
</div>
<br /><br />
<form id="AddROForm">
    <div class="form-group">

        <div class="row">
            <div class="col-lg-1"></div>
            <div class="col-lg-2">
                <label for="ControlNumber" class="control-label">Repair Order #: </label>
                <input for="ControlNumber" class="form-control" bind="@ro.ControlNumber" maxlength="15" required />
            </div>
            <div class="col-lg-1">
                <label for="TagNumber" class="control-label">Tag #: </label>
                <input for="TagNumber" class="form-control" bind="@ro.TagNumber" maxlength="8" />
            </div>
            <div class="col-lg-3">
                <label for="VIN" class="control-label">VIN: </label>
                <input for="VIN" class="form-control" bind="@ro.VIN" maxlength="18" />
                @*<small id="Chasis" class="form-text text-muted">@ro.GetChassisNumber()</small> figure out how to get chassis from vin after box looses focus*@
            </div>
            <div class="col-lg-2">
                <label for="Make" class="control-label">Make: </label>
                <input for="Make" class="form-control" bind="@ro.Make" maxlength="30" />
            </div>
            <div class="col-lg-2">
                <label for="Madel" class="control-label">Model: </label>
                <input for="Madel" class="form-control" bind="@ro.Madel" maxlength="30" />
            </div>
        </div>

        <div class="row AddRow">
            <div class="col-lg-1"></div>
            <div class="col-lg-4">
                <label for="Customer" class="control-label">Customer: </label>
                <input for="Custoemr" class="form-control" bind="@ro.Customer" maxlength="50" />
            </div>
            <div class="col-lg-2">
                <label asp-for="Location" class="control-label">Vehicle Location: </label>
                <select asp-for="Location" class="form-control" bind="@ro.Location">
                    <option value="">-- Select a Location --</option>
                    @foreach (var loc in Location)
                    {
                        <option value="@loc">@loc</option>
                    }
                </select>
            </div>
            <div class="col-lg-2">
                <label asp-for="Assigned" class="control-label">Assigned: </label>
                <select asp-for="Assigned" class="form-control" bind="@ro.Assigned">
                    <option value="">-- Select an Employee --</option>
                    @foreach (var emp in Employee)
                    {
                        <option value="@emp">@emp</option>
                    }
                </select>
            </div>
            <div class="col-lg-2">
                <label asp-for="Status" class="control-label">Status: </label>
                <select asp-for="Status" class="form-control" bind="@ro.Status">
                    <option value="">-- Select a Status --</option>
                    @foreach (var st in Status)
                    {
                        <option value="@st">@st</option>
                    }
                </select>
            </div>
        </div>
        <br />
        <div class="row"><div class="col-lg-1"></div><div class="col-lg-10"><hr id="Double" /></div></div>
        <div class="row">
            <div class="col-lg-1"></div>
            <div class="col-lg-10">
                @if (sections.Count > 0)
                {
                    foreach (var sec in sections)
                    {
                        <h3>Section @sec.SectionNumber</h3>

                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-5">
                                <label for="Failure" class="control-label">Failure: </label>
                                <input for="Failure" class="form-control" bind="@sec.Failure" readonly />
                            </div>
                            <div class="col-lg-1"></div>

                            <div class="col-lg-1">
                                <label><input type="checkbox" checked="@IsCApprovedChecked(sec)" />   Warranty</label>
                            </div>
                            <div class="col-lg-2">
                                <label><input type="checkbox" value="@IsWarrantyChecked(sec)" />   Repair Approved</label>
                            </div>
                        </div>

                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="AdminComments" class="control-label">Admin Comments: </label>
                                <input for="AdminComments" class="form-control" bind="@sec.AdminComments" readonly />
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="TechComments" class="control-label">Tech Comments: </label>
                                <input for="TechComments" class="form-control" bind="@sec.TechComments" readonly />
                            </div>
                        </div>
                    }
                }
            </div>
        </div>
        <div class="row"></div>
    </div>    @*Form-group*@
</form>
<div class="row">
    <div class="col-lg-1"></div>
    <div class="col-lg-9">
        <br /><br />
        <button class="btn btn-primary float-right" onclick="@AddSectionCalled">Add New Section</button>
    </div>
</div>

@if (isAddNew == true)
{
    <div class="modal" tabindex="-1" style="display:block" role="dialog">
        <div class="modal-dialog modal-dialog-scrollable modal-xl">
            <div class="modal-content">
                <div class="modal-header">
                    <h3 class="modal-title">New Repair Section</h3>
                    <button type="button" class="close" onclick="@CloseModal"><span aria-hidden="true">X</span></button>
                </div>
                <div class="modal-body">
                    <form>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-2">
                                <label for="sectionLetter" class="control-label">Section: </label>
                                <input for="sectionLetter" class="form-control" bind="@_newSection.SectionNumber" />
                            </div>
                            <div class="col-lg-1"></div>

                            <div class="col-lg-2">
                                <label><input type="checkbox" bind="@_newSection.Warranty" />   Warranty</label>
                            </div>
                            <div class="col-lg-2">
                                <label><input type="checkbox" bind="@_newSection.CustomerApproved" />   Repair Approved</label>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="_failure" class="control-label">Failure: </label>
                                <input for="_failure" class="form-control" bind="@_newSection.Failure" />
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="_adminComments" class="control-label">Admin Comments: </label>
                                <input for="_adminComments" class="form-control" bind="@_newSection.AdminComments" />
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-1"></div>
                            <div class="col-lg-10">
                                <label for="_techComments" class="control-label">Tech Comments: </label>
                                <input for="_techComments" class="form-control" bind="@_newSection.TechComments" />
                            </div>
                        </div>
                        <br />
                        <button class="btn btn-primary float-left" onclick="AddNewSection()">Add New Section</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
}

@functions
{


    private RepairOrder ro;
    private RepairOrder incomingRO;
    private RepairOrderDataAccess RoData;
    private string chassis;
    private List<string> Location;
    private List<string> Employee;
    private List<string> Status;
    private FileService fileService;
    private List<RepairSection> sections;
    private bool isAddNew;

    //for new repair section modal
    private RepairSection _newSection;

    protected override async Task OnInitAsync()
    {
        ro = new RepairOrder();
        Location = new List<string>();
        Employee = new List<string>();
        Status = new List<string>();
        sections = new List<RepairSection>();
        isAddNew = false;
        fileService = new FileService();
        RoData = new RepairOrderDataAccess(context);
        await LoadData();

    }

    private async Task LoadData()
    {
        Location = await Task.Run(() => fileService.ReadLocation());
        Employee = await Task.Run(() => fileService.ReadEmployees());
        Status = await Task.Run(() => fileService.ReadStatus());

    }

    public string IsCApprovedChecked(RepairSection sc)
    {
        if (sc.CustomerApproved == true)
        {
            return "checked";
        }
        else
        {
            return "";
        }
    }

    public string IsWarrantyChecked(RepairSection sc)
    {
        if (sc.Warranty == true)
        {
            return "checked";
        }
        else
        {
            return "";
        }
    }

    protected void AddSectionCalled()
    {
        _newSection = new RepairSection();
        this.isAddNew = true;
    }

    private void AddNewSection()
    {
        sections.Add(_newSection);
        this.StateHasChanged();            
        this.isAddNew = false;

    }



    private void CloseModal()
    {
        this.isAddNew = false;
    }

Answer

Mister Magoo picture Mister Magoo · Jun 20, 2019

There is a problem with the way you have bound the onclick event of the button on the modal form.

you have onclick="AddNewSection()" - writing it this way will likely be interpreted as a pure javascript call and if you inspect the DOM in your browser tools, you will likely see onclick="AddNewSection()" on the button.

You should have onclick="@AddNewSection" - this way, Blazor will hook up the onclick event to your C# method.