5

This post is entirely updated to put all code in one class and to try to make it as straight-forward as possible.

The project works perfectly EXCEPT for one thing: When a student record is updated, the database is updated, but the visible list does not update.

The only way I've been able to see the changes is when I refresh the entire page (see NavigationManager line at bottom of code).

Can you please help me understand what I'm not doing right here.

@page "/Students2"; @using ParentChild.Models; @inject IJSRuntime JS; // Used for Confirmation Dialog & Alert Box @inject NavigationManager NavigationManager // Used to Refresh Entire Page <h3>Students 2</h3> @if (byStudents == null) { <p><em>Loading...</em></p> } else if (!byStudents.Any()) { <p><em>No students in database. Please add some.</em></p> } else { <table class="table"> <thead> <tr> <th>First</th> <th>Last</th> <th>Street</th> <th>City</th> <th>State</th> <th>Zip</th> <th>Cell</th> <th>Home</th> <th>Email</th> <th>Note</th> <th></th> </tr> </thead> <tbody> @foreach (var byStudent in byStudents) { <tr> <td>@byStudent.SFirst</td> <td>@byStudent.SLast</td> <td>@byStudent.SStreet</td> <td>@byStudent.SCity</td> <td>@byStudent.SState</td> <td>@byStudent.SZip</td> <td>@byStudent.SCell</td> <td>@byStudent.SHome</td> <td>@byStudent.SEmail</td> <td>@byStudent.SNote</td> <td> <input type="button" class="btn btn-primary" @onclick="@(() => OpenEditStudent(@byStudent.SId))" value="Edit" /> <nbsp></nbsp> <input type="button" class="btn btn-danger" @onclick="@(async () => await DeleteStudentAction(@byStudent.SId))" value="Delete" /> </td> </tr> } </tbody> </table> } <button class="btn btn-primary" @onclick="() => OpenAddStudent()">Add New Student</button> @if (showBackdrop) { <div class="modal-backdrop fade show"></div> } <!-- Define Modal Pop-UP in this Page --> <div class="modal @modalClass" tabindex="-1" role="dialog" style="display:@modalDisplay; overflow-y: auto;"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title"> <!-- Pop-Up Title Section --> @{ var txt = ""; if ((SId == null) | (SId == "")) { txt = "Add Student"; @txt; } else { txt = "Edit Student"; @txt; } } </h5> <!-- Pop-Up Close Button at Top --> <button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> <span aria-hidden="true">&times;</span> </button> </div> <div class="modal-body"> <!-- Pop-Up Body Section --> <div class="form-group row"> <input id="SId" @bind="SId" type="hidden" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SFirst" class="col-sm-8 col-form-label">First Name:</label> <input id="SFirst" @bind="SFirst" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SLast" class="col-sm-8 col-form-label">Last Name:</label> <input id="SLast" @bind="SLast" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SStreet" class="col-sm-8 col-form-label">Street:</label> <input id="@SStreet" @bind="SStreet" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SCity" class="col-sm-8 col-form-label">City:</label> <input id="@SCity" @bind="SCity" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SState" class="col-sm-8 col-form-label">State:</label> <input id="@SState" @bind="SState" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SZip" class="col-sm-8 col-form-label">Zip:</label> <input id="@SZip" @bind="SZip" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SCell" class="col-sm-8 col-form-label">Cell #:</label> <input id="@SCell" @bind="SCell" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SHome" class="col-sm-8 col-form-label">Home #:</label> <input id="@SHome" @bind="SHome" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SEmail" class="col-sm-8 col-form-label">Email:</label> <input id="@SEmail" @bind="SEmail" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SNote" class="col-sm-8 col-form-label">Note:</label> <input id="@SNote" @bind="SNote" class="col-sm-4 form-control" /> </div> </div> <div class="modal-footer"> <!-- Pop-Up Body Section --> @{ if ((SId == null) | (SId == "")) { <button type="button" class="btn btn-primary" @onclick="async () => await AddStudentAction()">Add</button> } else { <button type="button" class="btn btn-primary" @onclick="async () => await UpdateStudentAction(this.SId)">Update</button> } } <button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => this.Close()">Close</button> </div> </div> </div> </div> @code { //------------------------------------------------------------------------- // Modal Control Variables and Code //------------------------------------------------------------------------- private string modalDisplay = "none;"; private string modalClass = ""; private bool showBackdrop = false; //--- Open Modal... public void Open() { modalDisplay = "block;"; modalClass = "show"; showBackdrop = true; } //--- Close Modal... public void Close() { modalDisplay = "none"; modalClass = ""; showBackdrop = false; } //------------------------------------------------------------------------- // Student List from Database //------------------------------------------------------------------------- DB_136837_byogaContext db = new DB_136837_byogaContext(); private List<ByStudents> byStudents; //--- Build Student List on Page Initialization... protected override void OnInitialized() { byStudents = db.ByStudents.ToList(); } //------------------------------------------------------------------------- // Modal Parameters //------------------------------------------------------------------------- private Modal modal { get; set; } [Parameter] public string SId { get; set; } = ""; [Parameter] public string SFirst { get; set; } = ""; [Parameter] public string SLast { get; set; } = ""; [Parameter] public string SStreet { get; set; } = ""; [Parameter] public string SCity { get; set; } = ""; [Parameter] public string SState { get; set; } = ""; [Parameter] public string SZip { get; set; } = ""; [Parameter] public string SCell { get; set; } = ""; [Parameter] public string SHome { get; set; } = ""; [Parameter] public string SEmail { get; set; } = ""; [Parameter] public string SNote { get; set; } = ""; //------------------------------------------------------------------------- // Open Modal to Add Student //------------------------------------------------------------------------- protected void OpenAddStudent() { //-- Clear Any Info Set in Previous Edit Requests... SId = ""; SFirst = ""; SLast = ""; SStreet = ""; SCity = ""; SState = ""; SZip = ""; SCell = ""; SHome = ""; SEmail = ""; SNote = ""; //--- Open Modal Popup... this.Open(); } //------------------------------------------------------------------------- // Open Modal to Edit Student //------------------------------------------------------------------------- protected void OpenEditStudent(int myID) { //--- Build & Run SQL Select Statement... using (var myContext = new DB_136837_byogaContext()) { //--- Grab Info for Selected Student... var myQuery = (from s in myContext.ByStudents where s.SId == myID select s).Single(); //--- Set Fields on Popup Form... this.SId = myQuery.SId.ToString(); this.SFirst = myQuery.SFirst; this.SLast = myQuery.SLast; this.SStreet = myQuery.SStreet; this.SCity = myQuery.SCity; this.SState = myQuery.SState; this.SZip = myQuery.SZip; this.SCell = myQuery.SCell; this.SHome = myQuery.SHome; this.SEmail = myQuery.SEmail; this.SNote = myQuery.SNote; } //--- Open Modal Form... this.Open(); } //------------------------------------------------------------------------- // Delete Selected Student from Database with Confirmation Dialog //------------------------------------------------------------------------- protected async Task DeleteStudentAction(int myID) { bool isConfirmed = await JS.InvokeAsync<bool>("confirm", $"Do you sure you want to permanently delete whis record?"); if (isConfirmed) { //--- Build & Run SQL Delete Statement... using (var myContext = new DB_136837_byogaContext()) { var myQuery = (from d in myContext.ByStudents where d.SId == myID select d).Single(); myContext.ByStudents.Remove(myQuery); myContext.SaveChanges(); } //--- Delete Complete Dialog... await JS.InvokeVoidAsync("alert", "Record Deleted"); //--- Refresh Page... await InvokeAsync(() => { StateHasChanged(); this.OnInitialized(); }); } } //------------------------------------------------------------------------- // Add Student to Database //------------------------------------------------------------------------- protected async Task AddStudentAction() { //--- Define the New Student... var newStudent = new ByStudents(); newStudent.SFirst = SFirst; newStudent.SLast = SLast; newStudent.SStreet = SStreet; newStudent.SCity = SCity; newStudent.SState = SState; newStudent.SZip = SZip; newStudent.SCell = SCell; newStudent.SHome = SHome; newStudent.SEmail = SEmail; newStudent.SNote = SNote; //--- Add New Student to Database... using (var myContext = new DB_136837_byogaContext()) { myContext.ByStudents.Add(newStudent); myContext.SaveChanges(); } //--- Close the Popup... this.Close(); //--- Refresh Page... await InvokeAsync(() => { StateHasChanged(); this.OnInitialized(); }); } //------------------------------------------------------------------------- // Update Student in Database //------------------------------------------------------------------------- protected async Task UpdateStudentAction(string myID) { //--- Build & Run SQL Select Statement... using (var myContext = new DB_136837_byogaContext()) { //--- Update Info for Selected Student... var myQuery = (from d in myContext.ByStudents where d.SId == Convert.ToInt32(myID) select d).Single(); myQuery.SFirst = this.SFirst; myQuery.SLast = this.SLast; myQuery.SStreet = this.SStreet; myQuery.SCity = this.SCity; myQuery.SState = this.SState; myQuery.SZip = this.SZip; myQuery.SCell = this.SCell; myQuery.SHome = this.SHome; myQuery.SEmail = this.SEmail; myQuery.SNote = this.SNote; myContext.ByStudents.Update(myQuery); myContext.SaveChanges(); } //--- Close the Popup... this.Close(); //--- Refresh Page... await InvokeAsync(() => { StateHasChanged(); this.OnInitialized(); }); //--- Refresh Page by Reloading...SHOULD NOT HAVE TO DO THIS! //NavigationManager.NavigateTo("Refresh/Students2"); } } 

Any and all help is appreciated. Thanks!

12
  • 1
    You shouldn't need InvokeAsync() or StateHasChanged() at all. Do you have some threading going on, like calling this with Task.Run() ? Commented Sep 9, 2020 at 5:54
  • 2
    Is the Modal popup in the same component code as the list? If not, that is probably the issue. Blazor updates only the component that raises an event, not all components. Think of StateHasChanged as "redraw this component and anything inside it, but leave anything upstream unchanged" Commented Sep 9, 2020 at 8:58
  • 1
    Is it indeed a screen update problem, ie does the actual Db operation work as expected? Commented Sep 10, 2020 at 19:20
  • 1
    Ammending my comment, I see you are calling OnInitialized again, however you are doing it after StateHasChanged(). Personally I would declare it like this private List<ByStudents> byStudents => db.ByStudents.ToList(); and then you can call StateHasChanged(); I don't think you need it in an InvokeAsync either as it's happening in the UI thread. Commented Jul 26, 2021 at 15:15
  • 1
    After update you can simply call byStudents = db.ByStudents.ToList(); Commented Oct 25, 2021 at 9:08

2 Answers 2

1

On your add/update page, after you are done with your operation and you are navigating back to your list, add the 'true' parameter to force a reload of the list page.

 NavigationManager.NavigateTo("students", true); 

See this link

Sign up to request clarification or add additional context in comments.

Comments

0

If I understand correctly, you have a child component modal that handles the crud responsible for data displayed by a parent component.

I believe if you call StateHasChanged() from a child component the state is updated in the child component's scope unless the parent is notified its state should be updated. I would try adding an eventcallback from your child component modal to the parent that triggers a StateHasChanged() on the parent level. You could manually invoke the callback on any child crud methods after the work is done.

Something similar to this: Blazor - Execute a parent component's method when a child component onclick event occurs

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.