I have a class / service in my Blazor MAUI application that regularly manipulates the data that is stored within it. There is an internal schedular on a fixed interval that regenerates the value.
I have a Blazor component that reads the value from this service. When the value changes in my service, I would like my Blazor component to reflect that change.
Just to keep it simple, lets take the following:
public class EmployeeService {
public int NumberOfEmployees { get; private set; }
public EmployeeService() {
// Logic to initialize a fixed scheduled for function
// RecalculateNumberOfEmployees();
}
private async void RecalculateNumberOfEmployees() {
numberOfEmployees = 1;
}
}
@path "/employees"
@inject EmployeeService Service
Number of employees: @Service.NumberOfEmployees
@code {
}
I found a recommendation here that uses a timer to invoke StateHasChanged()
but I really, really don't like that approach. It seems like it is a waste of resources and an anti-pattern.
My next step is to make EmployeeService
accept EventCallback
from the Blazor component and store that in a list. This would allow any component to listen for changes in the EmployeeService
class. When a component is unmounted, it will delete the callback.
Something like:
EmployeeService.cs
public List<EventCallback> listeners { get; private set; } = new List<EventCallback>();
public async void RegisterCallback(EventCallback callback) {
listeners.ForEach(...notify listeners);
}
public async void DeregisterCallback(EventCallback callback) {
listeners.Remove ... etc
}
Employees.razor
...
@code {
// register / deregister callback and listen for changes, invoke StateHasChanged()
}
Before I go down this route, are there any better design patterns that I could use for future components that would be better suited for this purpose? I feel like this is something that should already be baked into the framework but haven't seen anything that addresses it.
CodePudding user response:
You could use an event Action:
EmployeeService.cs
public class EmployeeService
{
public int NumberOfEmployees { get; private set; }
public EmployeeService() {
// Logic to initialize a fixed scheduled for function
// RecalculateNumberOfEmployees();
}
private async void RecalculateNumberOfEmployees() {
numberOfEmployees = 1;
OnChange();
}
public event Action Change;
private void OnChange() => Change?.Invoke();
}
Employees.razor
@inject EmployeeService EmployeeService
@implements IDisposable
Number of employees: @EmployeeService.NumberOfEmployees
@code {
protected override void OnInitialized()
{
EmployeeService.Change = EmployeeService_Change;
}
public void Dispose()
{
EmployeeService.Change -= EmployeeService_Change;
}
private async void EmployeeService_Change()
{
await InvokeAsync(StateHasChanged);
}
}
Another possibility is to use the Event Aggregator pattern. Take a look at this library: https://github.com/mikoskinen/Blazor.EventAggregator