In Blazor, how do I make changes to an injected object be reflected in all components? (AKA reactivity)

Deadly 提交于 2020-05-31 08:53:07

问题


In a Blazor WebAssembly site, suppose I have a class

    public class State : IState
    {
        public bool ShowEasterEggs { get; set; } = false;
    }

And I properly register it in my client's Program.cs: builder.Services.AddSingleton<IState, State>();

And I properly inject this into a component:

@inject IState State
@if (State.ShowEasterEggs)
{
  <span>EASTER EGGS SHOWN</span>
}

And in some other component, I enable a change to it:

@inject IState State
<input type="checkbox" @bind="State.ShowEasterEggs"/>Show Easter Eggs

I expect that a change to State.ShowEasterEggs from the second component would be detected and any instance of the first component would update automatically. But I find that I need to do some other interaction--changing other values in the first component's parent, to see the change in the first component's instance.

So how does this work, how should it work, and how can I get changes to injected objects to cause an update?


回答1:


You need to use Cascading values and parameters

You would need to have a CascadingValue component that is parent of any component that will react to the changes.

CascadingValue will receive a property called Value that will hold the value you want to the children to see (State).

And you will get that property in your child components and pass an attribute CascadingParameter to a property with the same name you passed in CascadingValue's Value attribute.

e.g. from the docs

The parent

@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses

@inject IState State @* Injecting the State *@

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-3">
            <NavMenu />
        </div>
        <div class="col-sm-9">
            <CascadingValue Value="State"> @* Passing State as a Cascading Value*@
                <div class="content px-4">
                    @Body
                </div>
            </CascadingValue>
        </div>
    </div>
</div>

Any child component

<input type="checkbox" @bind="State.ShowEasterEggs"/>Show Easter Eggs

@code {

    // Getting the Cascading Value as a parameter
    [CascadingParameter]
    protected State State { get; set; }

}



回答2:


There are various ways to do it. The following one is sort of a service that implements the State pattern and the Notifier pattern:

State.cs

 public interface IState
{
    event Action Notify;
    bool ShowEasterEggs { get; set; }
}
public class State : IState
{
    public event Action Notify;

    bool showEggs = false;
    public bool ShowEasterEggs
    {
        get => showEggs;
        set
        {
            if (showEggs != value)
            {
                showEggs = value;

                if (Notify != null)
                {
                    Notify?.Invoke();
                }
            }
        }
    }

}

Child1.razor

 @inject IState State

 <input type="checkbox" @bind="State.ShowEasterEggs" />Show Easter Eggs
 <hr />


 @code {

 }

Child2.razor

 @inject IState State
 @implements IDisposable

@if (State.ShowEasterEggs)
{
    <span>EASTER EGGS SHOWN</span>
}

<hr />


@code {
   protected override void OnInitialized()
  {
       State.Notify += OnNotify;
  }

 public void OnNotify()
 {
    InvokeAsync(() =>
    {
        StateHasChanged();
    });
}

public void Dispose()
{
    State.Notify -= OnNotify;
}
}

Index.razor

 @page "/"

 <Child1></Child1>
 <Child2></Child2>



 @code { }

Program.cs

builder.Services.AddSingleton<IState, State>();

AS you've probably noticed, the State service store the state of the check box, and notify subscribers of changes to this state.

Hope this helps...



来源:https://stackoverflow.com/questions/60674120/in-blazor-how-do-i-make-changes-to-an-injected-object-be-reflected-in-all-compo

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