【问题标题】:Blazor component : refresh parent when model is updated from child componentBlazor 组件:从子组件更新模型时刷新父组件
【发布时间】:2019-04-20 15:39:28
【问题描述】:

我在 ASP.NET Core 3 预览版 4 中使用服务器端 Blazor 组件。

我有一个父组件和子组件,使用相同的共享模型,如下所示:

型号:

public class CountModel
{
    public int Count { get; set; }

    public void Increment()
    {
        Count++;
    }
}

父组件:

@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();
}

子组件:

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

这是用于父子模型的同一个实例。 当模型从父模型更新时,两者都显示正确的增量值。 当它从孩子更新时,只有孩子显示正确的值。

如何在子组件更新父组件时强制刷新?

注意,这里我有一个更新模型的功能,但我希望解决方案在数据绑定到输入时起作用。

【问题讨论】:

  • 调用StateHasChanged 通知更改怎么样?喜欢 public void Increment() { Count++; StateHasChanged(); } 看看 Chris Sainty 的帖子 3 Ways to Communicate Between Components in Blazor 他使用它。也相关:github.com/aspnet/Blazor/issues/420
  • @dani herrera,我认为这个问题与不调用 StateHasChanged 无关。看我上面的答案...
  • 嗨 @Issac,我更喜欢委托而不是 CascadingParameter,我猜 Chris Sainty 级联示例完全符合这种情况。
  • 是的,我也代表...
  • 我知道有点晚了,但你可能想看看这个项目:github.com/dagmanolis/Blaast。它是您想要实现的目标的实现。

标签: c# asp.net-core blazor blazor-server-side


【解决方案1】:

创建共享服务。在父项中订阅服务的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();
}

【讨论】:

  • 我最终做了类似的事情,但不是共享服务,而是父组件订阅了 PropertyChanged 事件
  • 这对我也有用,但界面完全没有必要。
  • 这样注册服务的接口很有用; services.AddScoped();
  • @Shuvra 像这样:async () => await RefreshMethod();
  • 如果其他人遇到同样的问题,请确保您的服务未注册为“瞬态”,因为父子节点将拥有自己的服务实例,并且不会在两者之间触发事件组件。
【解决方案2】:

通过调用 StateHasChanged 方法更新父状态

创建一个方法来更新父节点的状态:

public void RefreshState(){
     this.StateHasChanged();
}

通过级联值或参数将父级传递给子级 示例:

<CascadingValue Value="this">
  <ChildComponent /> 
</CascadingValue>

现在在 Child 的组件上声明级联参数:

[CascadingParameter]
public ParentPageType _Parent { get; set; }

现在当你想刷新父级时,只需调用:

_Parent.RefreshState();

【讨论】:

  • 1.在'[CascadingParameter] public ParentPageType _Parent { get;放; }' ParentPageType 代表什么? 2. _Parent.RefreshState();然后从 Child 调用,对吗?
  • ParentPageType _Parent 由级联参数设置(自动设置),它是创建子组件的页面组件。 _Parent.RefreshState();是从孩子那里调用的。
  • 只是 Telerik Blazor TelerikWindow 组件的信息;这个 CascadingValue 在子组件中将为 null,因为 Telerik Window 呈现在应用程序的根目录,因此它的内容超出了原始 CascadingValue 组件的上下文。这是他们的解决方案docs.telerik.com/blazor-ui/knowledge-base/…
【解决方案3】:

以下代码 sn-p 是从子组件更新模型时刷新父组件的最合适方法。但它增加了更多的便宜:父母和孩子之间没有依赖关系。它不是专门为通知状态更改而创建的。它在属性、任何属性发生更改时通知它,它可以向订阅者提供值已更改的属性的名称、新值等。

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

这里要注意的重点是我们的模型类实现了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; }


}

希望这会有所帮助...

【讨论】:

    【解决方案4】:

    Cascading 参数的流向是向下的。为了让你的父组件被刷新,你想提供一个子组件可以调用的回调,并传递一些值。我已经在此处的 Blazor 部分中展示了如何在父组件上创建回调,以及如何触发回调,将值传递给它。

    【讨论】:

    • 感谢 Issac,经过一些测试,我更喜欢 Laurence73 的解决方案,因为当我使用服务时,父子之间没有直接依赖关系,并且它增加了更多的灵活性(例如,它是更容易处理兄弟组件)
    【解决方案5】:

    绑定可能是另一种更容易的方式。您可以做的是拥有一个作为对象的 [Parameter](而不是 [CascadingParameter])。您还声明了一个 Changed EventCallback

    [Parameter]
    public InputLength Model { get; set; }
    [Parameter]
    public EventCallback<InputLength> ModelChanged { get; set; }
    

    然后在你的类声明中的父级上 @bind-=SharedObject 的实例。

     <InputLength @bind-Model=inputLength></InputLength>
    

    最后一步是调用 Changed.InvokeAsync(object w/ updated data)。这在哪里完成取决于实际更新共享值的内容。

    public double Length;
    private double _Length
    {
        get { return Model.Length; }
        set { Model.Length = value; ModelChanged.InvokeAsync(Model); }
    }
    

    该调用将触发父对象的刷新并根据需要更新 UI。

    【讨论】:

      【解决方案6】:

      如果子组件调用父组件提供的 EventCallback,父组件会重新渲染。所以从技术上讲,你可以这样做:

      • 在子组件中,添加以下内容:
      [Parameter]
      public EventCallback OnIncrement { get; set; }
      
      • 然后在每次递增时调用它(伪代码如下):
      <button @onClick=@(args => HandleClick()) ... >
      Task HandleClick()
      {
          currentCount.Increment();
      
          return OnIncrement.InvokeAsync();
      }
      
      • 在父组件中,提供 OnIncrement 回调:
      <CountChild OnIncrement=@( () => {} )/>
      

      即使提供的回调什么都不做,它的调用也会触发重新渲染。

      请注意,“什么都不做”的方法/函数通常被认为是代码异味。

      【讨论】:

        【解决方案7】:

        这就是您实现所需功能的方法

        这里是Model,它有一个父组件将订阅的Action,以获取子组件所做的更改

         public class CountModel
         {
            public int Count { get; set; }    
            public Action Changed;
            
            public void Increment()
            {
               Count++;
               Changed.Invoke();
            }
         }
        

        这是父组件,父组件必须订阅Model才能获取count的变化

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

        【讨论】:

          猜你喜欢
          • 2019-11-15
          • 2022-01-13
          • 2021-07-04
          • 2020-04-01
          • 1970-01-01
          • 2020-01-31
          • 2017-05-30
          • 1970-01-01
          • 2023-02-20
          相关资源
          最近更新 更多