Blazor 组件:当模型从 child 组件更新时刷新 parent
Blazor component : refresh parent when model is updated from child component
我在 ASP.NET Core 3 预览版 4 中使用 Server-side Blazor 组件。
我有一个 parent 组件和 child 个组件,它们使用相同的共享模型,如下所示:
型号:
public class CountModel
{
public int Count { get; set; }
public void Increment()
{
Count++;
}
}
Parent 分量:
@page "/count"
<CascadingValue Value="currentCount">
<h1>Count parent</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@currentCount.Increment">+1 from parent</button>
<CountChild></CountChild>
</CascadingValue>
@functions {
private CountModel currentCount = new CountModel();
}
Child 分量:
<h1>Count child</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@currentCount.Increment">+1 from child</button>
@functions {
[CascadingParameter]
private CountModel currentCount { get; set; }
}
它是用于 parent 和 child 的同一模型实例。
当模型从 parent 更新时,两者都显示正确的增量值。
当它从 child 更新时,只有 child 显示正确的值。
如何在从 child 更新时强制刷新 parent 组件?
注意,这里我有一个更新模型的函数,但我希望解决方案在数据绑定到输入时起作用。
Cascading参数的流向是向下的。对于要刷新的父组件,您需要提供子组件可以调用的回调,并向其传递一些值。我已经在此处的 Blazor 部分展示了如何在父组件上创建回调,以及如何触发回调并向其传递一个值。
创建共享服务。在父项中订阅服务的 RefreshRequested
事件,在子项中订阅 Invoke() 。在父方法中调用 StateHasChanged();
public interface IMyService
{
event Action RefreshRequested;
void CallRequestRefresh();
}
public class MyService: IMyService
{
public event Action RefreshRequested;
public void CallRequestRefresh()
{
RefreshRequested?.Invoke();
}
}
//child component
MyService.CallRequestRefresh();
//parent component
MyService.RefreshRequested += RefreshMe;
private void RefreshMe()
{
StateHasChanged();
}
当模型从其 child 组件更新时,以下代码片段是刷新 parent 组件的最合适方法。但它增加了更多优惠:parent 和 child 之间没有依赖关系。它不是专门为通知状态更改而创建的。它在属性、任何属性发生变化时发出通知,并且它可以向订阅者提供其值发生变化的属性的名称、新值等
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.ComponentModel.DataAnnotations;
这里要注意的重点是我们的模型class实现了INotifyPropertyChanged接口...
CountModel.cs
public class CountModel : INotifyPropertyChanged
{
private int count;
public int Count
{
get => count;
set => SetProperty(ref count, value);
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string
propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public void Increment()
{
Count++;
}
}
Count.razor
@page "/count"
@implements IDisposable
<CascadingValue Value="currentCount">
<h1>Count parent</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" @onclick="@currentCount.Increment">+1
from parent</button>
<CountChild></CountChild>
</CascadingValue>
@code {
private CountModel currentCount = new CountModel();
protected override void OnInitialized()
{
currentCount.PropertyChanged += (sender, args) => StateHasChanged();
}
public void Dispose()
{
currentCount.PropertyChanged -= (sender, args) => StateHasChanged();
}
}
CountChild.razor
<h1>Count child</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" @onclick="@currentCount.Increment">+1 from
child</button>
@code {
[CascadingParameter]
private CountModel currentCount { get; set; }
}
希望这对您有所帮助...
通过调用 StateHasChanged 方法更新父状态
创建更新父级状态的方法:
public void RefreshState(){
this.StateHasChanged();
}
通过级联值或参数将父项传递给子项
示例:
<CascadingValue Value="this">
<ChildComponent />
</CascadingValue>
现在在子组件上声明级联参数:
[CascadingParameter]
public ParentPageType _Parent { get; set; }
现在当你想刷新父级时,只需调用:
_Parent.RefreshState();
绑定可能是另一种可能使它更容易的方法。您可以做的是拥有一个作为对象的 [Parameter](与 [CascadingParameter] 相对)。您还声明了一个 Changed EventCallback
[Parameter]
public InputLength Model { get; set; }
[Parameter]
public EventCallback<InputLength> ModelChanged { get; set; }
然后在您的 class 声明中的父级上 @bind-=SharedObject 的实例。
<InputLength @bind-Model=inputLength></InputLength>
最后一步是调用 Changed.InvokeAsync(带有更新数据的对象)。在哪里完成取决于实际更新共享值的内容。
public double Length;
private double _Length
{
get { return Model.Length; }
set { Model.Length = value; ModelChanged.InvokeAsync(Model); }
}
该调用将触发父对象的刷新并根据需要更新 UI。
父组件 re-renders 如果子组件调用父组件提供的 EventCallback。所以从技术上讲,你可以这样做:
- 在子组件中,添加以下内容:
[Parameter]
public EventCallback OnIncrement { get; set; }
- 然后每当你递增时调用它(伪代码如下):
<button @onClick=@(args => HandleClick()) ... >
Task HandleClick()
{
currentCount.Increment();
return OnIncrement.InvokeAsync();
}
- 在父组件中,提供 OnIncrement 回调:
<CountChild OnIncrement=@( () => {} )/>
即使提供的回调什么都不做,它的调用也会触发 re-render。
请注意,methods/functions“什么都不做”通常被认为是一种代码味道。
这就是您实现所需功能的方法
这是模型,它有一个父组件将订阅的动作,以获取子组件所做的更改
public class CountModel
{
public int Count { get; set; }
public Action Changed;
public void Increment()
{
Count++;
Changed.Invoke();
}
}
这是父组件,父组件必须订阅模型才能获得计数的变化
@page "/count"
<CascadingValue Value="currentCount">
<h1>Count parent</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@(()=>currentCount.Increment())">+1 from parent</button>
<Child></Child>
</CascadingValue>
@code {
private CountModel currentCount = new CountModel();
protected override void OnInitialized()
{
currentCount.Changed = StateHasChanged;
}
}
这是子组件
<h1>Count child</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@(()=>currentCount.Increment())">+1 from child</button>
@code {
[CascadingParameter]
private CountModel currentCount { get; set; }
}
我在 ASP.NET Core 3 预览版 4 中使用 Server-side Blazor 组件。
我有一个 parent 组件和 child 个组件,它们使用相同的共享模型,如下所示:
型号:
public class CountModel
{
public int Count { get; set; }
public void Increment()
{
Count++;
}
}
Parent 分量:
@page "/count"
<CascadingValue Value="currentCount">
<h1>Count parent</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@currentCount.Increment">+1 from parent</button>
<CountChild></CountChild>
</CascadingValue>
@functions {
private CountModel currentCount = new CountModel();
}
Child 分量:
<h1>Count child</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@currentCount.Increment">+1 from child</button>
@functions {
[CascadingParameter]
private CountModel currentCount { get; set; }
}
它是用于 parent 和 child 的同一模型实例。 当模型从 parent 更新时,两者都显示正确的增量值。 当它从 child 更新时,只有 child 显示正确的值。
如何在从 child 更新时强制刷新 parent 组件?
注意,这里我有一个更新模型的函数,但我希望解决方案在数据绑定到输入时起作用。
Cascading参数的流向是向下的。对于要刷新的父组件,您需要提供子组件可以调用的回调,并向其传递一些值。我已经在此处的 Blazor 部分展示了如何在父组件上创建回调,以及如何触发回调并向其传递一个值。
创建共享服务。在父项中订阅服务的 RefreshRequested
事件,在子项中订阅 Invoke() 。在父方法中调用 StateHasChanged();
public interface IMyService
{
event Action RefreshRequested;
void CallRequestRefresh();
}
public class MyService: IMyService
{
public event Action RefreshRequested;
public void CallRequestRefresh()
{
RefreshRequested?.Invoke();
}
}
//child component
MyService.CallRequestRefresh();
//parent component
MyService.RefreshRequested += RefreshMe;
private void RefreshMe()
{
StateHasChanged();
}
当模型从其 child 组件更新时,以下代码片段是刷新 parent 组件的最合适方法。但它增加了更多优惠:parent 和 child 之间没有依赖关系。它不是专门为通知状态更改而创建的。它在属性、任何属性发生变化时发出通知,并且它可以向订阅者提供其值发生变化的属性的名称、新值等
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.ComponentModel.DataAnnotations;
这里要注意的重点是我们的模型class实现了INotifyPropertyChanged接口...
CountModel.cs
public class CountModel : INotifyPropertyChanged
{
private int count;
public int Count
{
get => count;
set => SetProperty(ref count, value);
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string
propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public void Increment()
{
Count++;
}
}
Count.razor
@page "/count"
@implements IDisposable
<CascadingValue Value="currentCount">
<h1>Count parent</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" @onclick="@currentCount.Increment">+1
from parent</button>
<CountChild></CountChild>
</CascadingValue>
@code {
private CountModel currentCount = new CountModel();
protected override void OnInitialized()
{
currentCount.PropertyChanged += (sender, args) => StateHasChanged();
}
public void Dispose()
{
currentCount.PropertyChanged -= (sender, args) => StateHasChanged();
}
}
CountChild.razor
<h1>Count child</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" @onclick="@currentCount.Increment">+1 from
child</button>
@code {
[CascadingParameter]
private CountModel currentCount { get; set; }
}
希望这对您有所帮助...
通过调用 StateHasChanged 方法更新父状态
创建更新父级状态的方法:
public void RefreshState(){
this.StateHasChanged();
}
通过级联值或参数将父项传递给子项 示例:
<CascadingValue Value="this">
<ChildComponent />
</CascadingValue>
现在在子组件上声明级联参数:
[CascadingParameter]
public ParentPageType _Parent { get; set; }
现在当你想刷新父级时,只需调用:
_Parent.RefreshState();
绑定可能是另一种可能使它更容易的方法。您可以做的是拥有一个作为对象的 [Parameter](与 [CascadingParameter] 相对)。您还声明了一个 Changed EventCallback
[Parameter]
public InputLength Model { get; set; }
[Parameter]
public EventCallback<InputLength> ModelChanged { get; set; }
然后在您的 class 声明中的父级上 @bind-=SharedObject 的实例。
<InputLength @bind-Model=inputLength></InputLength>
最后一步是调用 Changed.InvokeAsync(带有更新数据的对象)。在哪里完成取决于实际更新共享值的内容。
public double Length;
private double _Length
{
get { return Model.Length; }
set { Model.Length = value; ModelChanged.InvokeAsync(Model); }
}
该调用将触发父对象的刷新并根据需要更新 UI。
父组件 re-renders 如果子组件调用父组件提供的 EventCallback。所以从技术上讲,你可以这样做:
- 在子组件中,添加以下内容:
[Parameter]
public EventCallback OnIncrement { get; set; }
- 然后每当你递增时调用它(伪代码如下):
<button @onClick=@(args => HandleClick()) ... >
Task HandleClick()
{
currentCount.Increment();
return OnIncrement.InvokeAsync();
}
- 在父组件中,提供 OnIncrement 回调:
<CountChild OnIncrement=@( () => {} )/>
即使提供的回调什么都不做,它的调用也会触发 re-render。
请注意,methods/functions“什么都不做”通常被认为是一种代码味道。
这就是您实现所需功能的方法
这是模型,它有一个父组件将订阅的动作,以获取子组件所做的更改
public class CountModel
{
public int Count { get; set; }
public Action Changed;
public void Increment()
{
Count++;
Changed.Invoke();
}
}
这是父组件,父组件必须订阅模型才能获得计数的变化
@page "/count"
<CascadingValue Value="currentCount">
<h1>Count parent</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@(()=>currentCount.Increment())">+1 from parent</button>
<Child></Child>
</CascadingValue>
@code {
private CountModel currentCount = new CountModel();
protected override void OnInitialized()
{
currentCount.Changed = StateHasChanged;
}
}
这是子组件
<h1>Count child</h1>
<p>Current count is : @currentCount.Count</p>
<button class="btn btn-primary" onclick="@(()=>currentCount.Increment())">+1 from child</button>
@code {
[CascadingParameter]
private CountModel currentCount { get; set; }
}