Automatically refresh a component in Blazor
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.
Related videos on Youtube
Dwayne Barsotta
Updated on June 04, 2022Comments
-
Dwayne Barsotta almost 2 years
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; }
-
Henk Holterman almost 5 yearsI think you should show the flow of data (_newSection) a little better. Also, what library or example did you use for the Modal comp?
-
dani herrera almost 5 yearsCan you rewrite question as a minimal reproducible example? I mean, without modal and with minimal html and minimal
RepairSection
fields. -
Mister Magoo almost 5 yearsWhich component contains the method AddNewSection?
-
Dwayne Barsotta almost 5 years@MisterMagoo the AddNewSection() method is in the page's functions section.
-
Mister Magoo almost 5 yearsChange your onclick on the Add New Section button to
onclick="@AddNewSection"
-
-
Mister Magoo almost 5 yearsYou should not be using
form
elements in Blazor without at least a null method to prevent submit behaviour - if you really needform
elements (I don't think you do) then add a null method likeonsubmit="@(()=>{})"
oronsubmit="@DoNothing"
where DoNothing() does nothing - this will prevent default form submit behaviour. -
Dwayne Barsotta almost 5 yearsAt first i left the "()" in the onclick="@AddNewSection" - once I removed it things worked better. Now when I hit the modal goes away and all the text (even the text in the fields for the RepairOrder disappears. I commented out the StateHasChanged() in the AddNewSection method. Now when I click "AddNewSection" the page shows the new section for a split second and then refresh's - clearign out all the data on the page. --Just read above after I saved! I think this is my issue!!
-
Mister Magoo almost 5 yearsPlease update your question with specific version numbers for .NETCore SDK , Visual Studio and the Blazor extension for VS - also include what PackageReferences you have in the csproj
-
Mister Magoo almost 5 yearsRemove the
form
elements - see my comment above - they are likely causing a full page refresh -
Dwayne Barsotta almost 5 yearsRemoving the form tags form the page did the trick!!! Thank you very much. I am accepting this as the correct answer!
-
Henk Holterman almost 5 yearsFrom preview 6 onward, it should now be
@onclick="@AddNewSection"