Open Closed

How do I force the Blazor toolbar to refresh #3592


User avatar
0
TonyH created

I have a ToolbarItem that has a text string bound to an extension method attached to my CurrentUser. It only updates if I specifically call StateHasChanged from within the component itself, eg. by adding a click event:

@inject ICurrentUser _currentUser; <div class="btn"> <i class="far fa-chart-pie" @onclick="ShowNotifications">@_currentUser.CurrentDivision()</i> </div> @code { private async Task ShowNotifications() { await Message.Info("TODO: Switch Divisions"); StateHasChanged(); }

Changing the value of CurrentDivision outside of the component doesn't trigger a refresh.

There is only one point in the application where this value changes, when a user selects a value from a dropdown list. I tried getting a reference to the component, with the view that I would add a "Refresh" method to it that would call StateHasChanged ... but I can't cast the object:

var toolbar = _toolbarManager.GetAsync("Main"); var item = toolbar.Result.Items[0]; ToolbarNotificationDivision notification = item as ToolbarNotificationDivision; // this won't compile

Is there another way I can force a StateHasChanged on the main toolbar component? Or, preferably, call a custom method (eg. "Refresh") on a toolbar component?

Thanks!

T


3 Answer(s)
  • User Avatar
    2
    enisn created
    Support Team .NET Developer

    Hi @TonyH

    You can publish an event inside the application to call StateHasChanged() method.

    I'll share an example for UnreadCount for notifications. Firstly I'll create a NotificationData class and register it as scoped into service collection. Scoped is important here because if you define it as Transient, a new instance will be created each time and it can't keep its state. If you define it as Singleton, it's ok in Blazor WASM but it won't work on Blazor Server, all the connected users will use the same instance of that object. So, Scoped is the best way in here.

    1- Define NotificationData.cs first.

    public class NotificationData : IScopedDependency
    {
        private int unreadCount;
    
        public int UnreadCount
        {
            get => unreadCount;
            set
            {
                unreadCount = value;
                UnreadCountChanged?.Invoke(this, new EventArgs());
            }
        }
    
        public event EventHandler UnreadCountChanged;
    }
    

    2- Create a notification component with name NotificationsComponent.razor for toolbar

    @inject NotificationData NotificationData
    
    <div class="nav-link">
        <i class="fa fa-bell"></i>
    
        @if (NotificationData.UnreadCount > 0)
        {
            <span class="position-absolute top-0 badge rounded-pill bg-danger">
                @NotificationData.UnreadCount
                <span class="visually-hidden">unread messages</span>
            </span>
        }
    </div>
    
    @code{
        protected override Task OnInitializedAsync()
        {
            NotificationData.UnreadCountChanged += (s, e) => StateHasChanged();
            return base.OnInitializedAsync();
        }
    }
    

    3- Add it to the toolbar

    public class MyToolbarContributor : IToolbarContributor
    {
        public Task ConfigureToolbarAsync(IToolbarConfigurationContext context)
        {
            if(context.Toolbar.Name == StandardToolbars.Main)
            {
                context.Toolbar.Items.Add(new ToolbarItem(typeof(NotificationsComponent)));
            }
    
            return Task.CompletedTask;
        }
    }
    

    4- Don't forget to configure it in the module file

    Configure<AbpToolbarOptions>(options =>
    {
        options.Contributors.Add(new MyToolbarContributor());
    });
    

    5- Inject the NotificationData wherever you want and make changes. (In my case I've injected it into Index.razor)

    @inject NotificationData NotificationData
    
    <Button Color="Color.Success" @onclick="@(()=> NotificationData.UnreadCount++)" >Increase Unread Count</Button>
    <Button Color="Color.Danger" @onclick="@(()=> NotificationData.UnreadCount--)" >Decrease Unread Count</Button>
    

    6- See the result:

  • User Avatar
    2
    TonyH created

    PERFECT!

    Not only is this exactly what I needed to achieve, your explanation and code samples are some of the best explanations I've seen so far. Nothing assumed, nothing left out.

    Might be worth adding this sample to your official documentation?

    Thanks!

    T

  • User Avatar
    1
    enisn created
    Support Team .NET Developer

    Thanks for your feedback.

    Additionally, Local Event Bus can be used in this scenario, but regular csharp events are much more useful for UI changes. Also, creating a new object like NotificationData allows to manage component's data independently from the component, so it's an abstraction over UI.

Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11