Marco Jr
Marco Jr

Reputation: 6796

How to get updated property in singleton from a component in Blazor?

I just changed the default Blazor sample to illustrate this issue.

MainLayout.razor (This is the top of the page, just replaced the About to display the counter)

@inherits LayoutComponentBase
@inject TestClass testClass
<div class="sidebar">
   <NavMenu />
</div>

<div class="main">
  <div class="top-row px-4">
    Counter: @testClass.Counter
</div>

<div class="content px-4">
    @Body
</div>

Counter.razor (This one was modified to instead use the local variable, invoke the method to increment via class and display the value from the class)

@page "/counter"
@inject TestClass testClass
<h1>Counter</h1>

<p>Current count:@testClass.Counter</p>

<button class="btn btn-primary" @onclick="(e => testClass.Increment())">Click me</button>

@code {

}

TestClass.cs (This one is the class with the current value and method to increment)

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks;

namespace TestBlazor.Classes
{
   public class TestClass
    {
     public int Counter { get; set; } = 10;
     public void Increment() {
        Counter++;
     }
 }
}

This is the result after few presses on the increment button:

enter image description here

So, the page itself can display the current value every time it's changes, however the component on the top of the page cannot. It's just displays the default value of 10.

This is also my Program.cs

using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using TestBlazor.Classes;

namespace TestBlazor
{
  public class Program
  {
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.RootComponents.Add<App>("app");

        builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
        builder.Services.AddSingleton<TestClass>();

        await builder.Build().RunAsync();
    }
  }
}

Using Core5

Upvotes: 1

Views: 1351

Answers (1)

enet
enet

Reputation: 45626

You'll need to notify the MainLayout component that its state has changed, and that he should re-render. This is doable by defining an event delegate that is executed whenever the counter variable is incremented:

TestClass.cs

public class TestClass
    {
        public int Counter { get; set; } = 10;
        public void Increment()
        {
            Counter++;
            Notify?.Invoke();
        }
        public event Action Notify;
    }

Add this code to the MainLayout page

@code{
    protected override void OnInitialized()
    {
        testClass.Notify += () => InvokeAsync(StateHasChanged);

        base.OnInitialized();
    }
}

You'll also have to implement IDisposable

Upvotes: 4

Related Questions