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;
}
}
希望这有用...
使用网络 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;
}
}
希望这有用...