Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blazor - razor page not updating after property from DI Service is changed

Using dotnet 3.1.100-preview2-014569

Ok consider the following example:

Create a new Blazor WebAssemply project from template, then add the following:

books.razor

@page "/books"
@inject BookService bookService

@if (bookService.isLoaned)
{
    <p><em>Book loaned</em></p>
}
else
{
    <p><em>Book returned</em></p>
}

BookService.cs

public class BookService
    {
        public int BookId { get; set; }

        public string Title { get; set; }

        public bool isLoaned { get; set; }
    }

Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<BookService>();
        }

NavMenu.razor

@inject BookService bookService;
<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorBlank_PV2</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="books" @onclick="LoanBookClicked">
                <span class="oi oi-plus" aria-hidden="true"></span>Loan Book
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="books" @onclick="ReturnBookClicked">
                <span class="oi oi-list-rich" aria-hidden="true"></span>Return Book
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }

    private void LoanBookClicked()
    {
        bookService.isLoaned = true;       
    }
    private void ReturnBookClicked()
    {
        bookService.isLoaned = false;
    }


}

What happens: Not rendering issue

Expected behavior: When clicking on the menu item Loan Book the page should show Book Loaned or if I click Return Book it should say Book returned. But this only happens if I click to another page, like Home, and then back again.

How can I force the page to re-render/re-check updated values from BookService even when it's on the same page already?

Thanks!

like image 790
Morten_564834 Avatar asked Nov 06 '19 09:11

Morten_564834


People also ask

How do you refresh a page in Blazor?

A page is reloaded/refreshed automatically at a specified interval using “NavigationManager” in OnAfterRender() method. Here the NavigateTo(“url”, forceLoad: true) method, is used to force load the browser based on the URI.

How do I refresh the Blazor component without reloading the page when database is updated?

How do I refresh the Blazor component without reloading the page when database is updated? You can refresh the Blazor component using the SignalR concept without reloading the page. When using SignalR, saving the changes in one page notifies the changes made to other clients.

How do you Rerender a component in Blazor?

To force a component to rerender, use the “StateHasChanged” method in Blazor, to notify that the state has been changed and requires re-rendering.

Can you mix Blazor and Razor?

Razor components can be integrated into Razor Pages and MVC apps in a hosted Blazor WebAssembly solution. When the page or view is rendered, components can be prerendered at the same time.


1 Answers

Here's a new solution in which I implement the INotifyPropertyChanged intereface in the BookService class. Why use this interface ?

  • It can be applied to other properties as well

  • Notifying clients that a property value has changed is essential part of good services, and this is not limited to only call the StateHasChanged solely for this purpose.

  • With the INotifyPropertyChanged intereface implemented, I can pass event data to registered or subscribing objects.

Important: Calling a method to update a property value like bookService.SetLoanedState(false) is a bad programming and an anti-patterns design. A property can have two accessors, get and set, and they should be used to get a value and to change a value. Methods have different roles...

BookService.cs


using System.ComponentModel;
using System.Runtime.CompilerServices;



public class BookService : INotifyPropertyChanged
    {
        private bool isLoaned;
        public event PropertyChangedEventHandler PropertyChanged;

        public int BookId { get; set; }

        public string Title { get; set; }


        public bool IsLoaned
        {
            get
            {
                return this.isLoaned;
            }

            set
            {
                if (value != this.isLoaned)
                {
                    this.isLoaned = value;
                    NotifyPropertyChanged();
                }
            }
        }


        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Books.razor

@page "/books"
@inject BookService bookService
@using System.ComponentModel


@if (bookService.IsLoaned)
{
    <p><em>Book loaned</em></p>
}
else
{
    <p><em>Book returned</em></p>
}

@code{

    protected override void OnInitialized()
    {
        bookService.PropertyChanged += PropertyHasChanged;
    }
    private void PropertyHasChanged(object sender, PropertyChangedEventArgs args)
    {
         StateHasChanged();
    }
}

NavMenu.razor

@code {

    // Code removed for brevity

    private void LoanBookClicked()
    {
        bookService.IsLoaned = true;      
    }
    private void ReturnBookClicked()
    {
        bookService.IsLoaned = false;      
    }
}

Hope this works...

like image 130
enet Avatar answered Oct 14 '22 16:10

enet