Blazor - 在来自 DI 服务的 属性 更改后,razor 页面未更新

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

使用网络 3.1.100-preview2-014569

好的,请考虑以下示例:

从模板创建一个新的 Blazor WebAssemply 项目,然后添加以下内容:

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;
    }


}

发生了什么:

预期行为: 单击菜单项 Loan Book 时,页面应显示 Book Loaned,或者如果我单击 Return Book,则应显示 Book returned。但这只有在我单击另一个页面(例如主页)然后再次返回时才会发生。

如何强制页面 re-render/re-check 从 BookService 更新值,即使它已经在同一页面上?

谢谢!

books.razor(为图书服务的新 onchange 事件添加处理程序)

@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(添加一个事件动作,将属性改为只读,添加一个setter,触发事件将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(改为使用服务 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);
    }


}

有关更多信息,请查看 Chris Sainty 的博客 post here 这非常清楚

这是我在 BookService class 中实现 INotifyPropertyChanged 接口的新解决方案。为什么要使用这个界面?

  • 它也可以应用于其他属性

  • 通知客户 属性 值已更改是良好服务的重要组成部分,这不仅限于仅为此目的调用 StateHasChanged。

  • 通过实施 INotifyPropertyChanged 接口,我可以将事件数据传递给已注册或订阅的对象。

重要提示: 调用方法来更新 属性 值,如 bookService.SetLoanedState(false) 是糟糕的编程和反模式设计。 属性 可以有两个访问器,get 和 set,它们应该用于获取值和更改值。方法有不同的作用...

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;      
    }
}

希望这有用...