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

隐身守侯 提交于 2020-01-30 07:49:06

问题


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:

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!


回答1:


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...




回答2:


books.razor (add a handler for the new onchange event of the book service )

@page "/books"
@inject BookService bookService

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

@code{
 protected override void OnInitialized()
    {
        bookService.OnChange += StateHasChanged;
    }
}

BookService.cs (add an event action,change the property to readonly, add a setter, trigger event will NotifyStateChanged()

public class BookService
    {   
        public event Action OnChange;

        public int BookId { get; set; }

        public string Title { get; set; }

        public bool isLoaned { get; }

        public void SetLoanedState(bool State)
        {
           isLoaned  = State;
           NotifyStateChanged();
        }

        private void NotifyStateChanged() => OnChange?.Invoke();

    }

NavMenu.razor (change to use service SetLoanedState()

@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.SetLoanedState(true);       
    }
    private void ReturnBookClicked()
    {
        bookService.SetLoanedState(false);
    }


}

for more information look at Chris Sainty's blog post here it makes this really clear



来源:https://stackoverflow.com/questions/58726847/blazor-razor-page-not-updating-after-property-from-di-service-is-changed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!