I am working on a .net5 Blazor WebApp using the MudBlazor library.
I'm trying to create a nav menu that displays certain categories and category pages. But as there is the possibility to add new categories, or add new category pages I need to refresh my component when the info has changed. When calling the function in OnInitializedAsync()
this has no problem with rendering the nav menu as it's supposed to. But when calling it again after updating any of the information that it should render, this function no longer seems to do what it's supposed to do, which is re-rendering the component. Now, the easiest solution could be that I can simply refresh the whole page. But this isn't what I want as there is other logic that needs to keep running without being interfered with by the reload of the page. My .razor file looks like the following:
@inject CategoryService categoryService
@inject CategoryPageService categoryPageService
@inherits LayoutComponentBase
<MudText Typo="Typo.h6" Class="px-4" Style="margin-bottom: 10px; margin-top: 10px; text-align:center">Categories</MudText>
<MudDivider />
<MudNavMenu Style="color:white">
@foreach (Category category in NavCategories)
{
@if(NavCategoryPages.Count > 0)
{
@foreach(CategoryPage categoryPage in NavCategoryPages)
{
@if(categoryPage.CategoryId == category.Id)
{
<MudNavGroup Title=@category.Name>
@foreach(CategoryPage categoryPage1 in NavCategoryPages.Where(c => c.CategoryId == category.Id))
{
<MudNavLink>@categoryPage1.PageName</MudNavLink>
}
</MudNavGroup>
}
else
{
<MudNavLink>@category.Name</MudNavLink>
}
}
}
}
</MudNavMenu>
@code
{
private List<Category> NavCategories = new List<Category>();
private List<CategoryPage> NavCategoryPages = new List<CategoryPage>();
protected override async Task OnInitializedAsync()
{
await GetCategoriesNav(); //Function that should grab the new information from the database
}
public async Task GetCategoriesNav()
{
NavCategories = await categoryService.GetCategories();
NavCategoryPages = await categoryPageService.GetCategoryPages();
//This delay is to have enough time to view if the StateHasChanged has any effect on first call.
await Task.Delay(5000);
StateHasChanged();
}
}
I've double-checked all the values which they have to display and they show up accordingly in the debug. If you need any extra information don't hesitate to ask.
The first call is being made in:
protected override async Task OnInitializedAsync()
{
await GetCategoriesNav();
}
This call renders the NavMenu as it's supposed to. After that, the only time it's being called somewhere else is when I edit/add a category. This is done in:
//These 2 functions are called via a button.
async Task AddCategory()
{
Category thisCategory = new();
var param = new DialogParameters { ["category"] = thisCategory };
IDialogReference dialog = DialogService.Show<CategoryDialog>("Add Category", param);
DialogResult result = await dialog.Result;
if(!result.Cancelled)
{
GetCategories();
//if a category has succesfully been added, it calls the same method which also gets called in the "OnInitializedAsync()"
await GetCategoriesNav();
}
}
async Task EditCategory(Category category)
{
category = await categoryService.EditCategory(category);
var param = new DialogParameters { ["category"] = category };
var dialog = DialogService.Show<CategoryDialog>("Edit Category", param);
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
GetCategories();
//if a category has succesfully been edited, it calls the same method which also gets called in the "OnInitializedAsync()"
await GetCategoriesNav();
}
}
This here is the only external place this is being called, but CategoryAdministration inherits from Category selector.
I assume you have a management page CategoryAdmin.razor with buttons that open Dialogs with an editor - Category Dialog.
Within that page you have a component - I assume called NavCategoriesComponent
- that displays the NavCategories.
You click on one of your buttons and edit/add a category, but on exit from the dialog the list in the NavCategories component doesn't update.
You also have a CategoryService
that I'm assuming gets the list of categories.
In NavCategoriesComponent
you code looks like this:
private List<Category> NavCategories = new List<Category>();
private List<CategoryPage> NavCategoryPages = new List<CategoryPage>();
//OnInit code
NavCategories = await categoryService.GetCategories();
NavCategoryPages = await categoryPageService.GetCategoryPages();
//not required
await Task.Delay(5000);
and then in your main component you call the following code for add/edit:
GetCategories();
//if a category has succesfully been edited, it calls the same method which also gets called in the "OnInitializedAsync()"
await GetCategoriesNav();
So which list are you expecting the second calls to update? The lists in NavCategoriesComponent
are a totaly separate. Calling GetCategories() after editing doesn't update the list in NavCategoriesComponent
. That only happens when you reload the page. OnInitializedAsync
as the name suggests only gets run once.
If that's your basic scenario then:
List<Category
and List<CategoryPage>
lists need to live in CategoryService
. Only one version of the truth now.await categoryService.GetCategories()
to get that list when you need it populated.NavCategoriesComponent
.event
say RecordListChanged
to CategoryService
.NavCategoriesComponent
and call StateHasChaged
on that event.CategoryService
lists and trigger the RecordListChanged
event.You should never manually call OnInitializedAsync
, and you should rarely need to call StateHasChanged
.
This Github Repo contains a solution that demos the above principles in the good old Blazor WeatherReport app - Blazr.Demo.DBNotification