如何在同一Blazor页面上的Razor组件之间传递数据?

11

我有这个Blazor页面

@page "/bearoffdata"
@using BlazorBoinq.Components

<h3>Bearoff Data</h3>

<Position_Hex_IdPair />

<PositionData />

@code {

}

使用这两个 Razor 组件:

@using BlazorBoinq.Data
@using BgBearoffCoreNamespace;
@inject BgBearoffService BoService

<label>Position</label>

<input type="text" spellcheck="false" @bind-value="@PositionText" @bind-value:event="oninput" />

<span> = </span>

<input type="number" step="1" @bind-value="@PositionId" @bind-value:event="oninput" />

<label>Id</label>

@code {

    BgBearoffCore BgBo;

    protected override async Task OnInitializedAsync()
    {
        BgBo = await BoService.GetBgBearoffAsync();
    }

    private Int64 positionId;
    private String positionText;

    protected Int64 PositionId
    {
        get => positionId;
        set
        {
            positionId = value;
            if (positionId > 0 && positionId <= BgBo.MaxId)
            {
                positionText = BgBearoffCore.menOnPointToHexString(BgBo.getMenOnPointFromInvariantId(positionId));
            }
            else
                positionText = "";
        }
    }

    protected String PositionText
    {
        get => positionText;
        set
        {
            positionText = value;
            if (BgBo.IsValidHexPosition(positionText))
                positionId = BgBo.getInvariantIdFromPosition(positionText);
            else
                positionId = 0;
        }
    }
}

@using BlazorBoinq.Data
@using BgBearoffCoreNamespace;
@inject BgBearoffService BoService

<button class="btn btn-primary" @onclick="ShowBearoffInfo">Show Data</button>

<br>

<textarea cols="36" rows="36" readonly @bind="@BearoffInfo" />


@code {
    BgBearoffCore BgBo;

    protected override async Task OnInitializedAsync()
    {
        BgBo = await BoService.GetBgBearoffAsync();
    }


    private String bearoffInfo = "";
    public String BearoffInfo
    {
        get => bearoffInfo;
        set { }
    }
    protected void ShowBearoffInfo()
    {
        bearoffInfo = BgBo.getPositionInformationText(86);
    }
}

我想把第一个组件的PositionId传递给第二个组件,这样我就可以用PositionId参数替换最后一行中的硬编码86。

3个回答

13

这里有一个可能的解决方案,因为您可以访问正在使用的组件代码,所以这个解决方案可能是可行的。

此解决方案包括三个步骤:

  1. 在第一个组件中定义事件回调。
  2. 在第二个组件中定义参数。
  3. 在父组件(即您的页面)中定义属性。

步骤1:在第一个组件中定义事件回调。

这样可以在属性更改时通知父组件(即您的页面)。

将您的PositionId属性声明为公共参数。

[Parameter] public int PositionId

你可以将你的getter和setter保留不变。

将你的输入更改为此:

<input type="text" spellcheck="false" @oninput="OnPositionIdChanged" />

像这样声明事件回调函数:

[Parameter] public EventCallback<int> PositionIdChanged { get; set; }

然后定义一个处理更改的方法,像这样:

private Task OnPositionIdChanged(ChangeEventArgs e)
{
    PositionId = int.Parse(e.Value.ToString());
    return PositionIdChanged.InvokeAsync(PositionId);
}

现在,当输入框中的值发生改变时,将引发一个EventCallback事件。

步骤2:在第二个组件中定义一个参数。

这将允许您从父组件(即您的页面)向第二个组件传递一个值。

像这样声明一个公共参数:

[Parameter] public int APositionId {get; set; }

步骤3:在您的父组件(即页面)中定义一个属性。

在这里,您需要定义一个属性,并在您的第一个组件中的属性值更改时更新它,然后将该值提供给您的第二个组件参数。

像这样在您的页面中定义一个属性:

private int SuppliedPosition { get; set; }

将其连接到第一个组件的更改通知器中,如下所示:

<Position_Hex_IdPair @bind-PositionId="SuppliedPosition"  />

将其提供给第二个组件中的参数,如下所示:

<PositionData APositionId="@SuppliedPosition"/>

我已经将每个附加属性的名称稍微不同地命名,这样希望清楚哪个是哪个。

就是这样!这种解决方案的缺点是需要你更改组件并在页面上添加代码。

有关事件回调和参数的更多信息,请参阅Blazor文档: Blazor文档

希望这可以帮助到您。


我将尝试事件回调方法。我应该提到第二个组件有一个按钮,触发从第一个组件检索id的操作。 - Hal Heinrich
谢谢,那个起作用了!我舍弃了Task OnPositionIdChanged,并且从第一个组件的setter中调用了PositionIdChanged.InvokeAsync。 - Hal Heinrich

10

是的,你有两个控件没有直接关联,所以不能简单地传递参数。

两个选择:

级联参数:https://learn.microsoft.com/zh-cn/aspnet/core/blazor/components?view=aspnetcore-5.0#cascading-values-and-parameters

或者状态管理。对于状态管理, 可以参考这个: 在 Blazor 中实现状态管理

你需要一个像这样的类:

using System;
public class CounterState
{
    // _currentCount holds the current counter value
    // for the entire application
    private int _currentCount = 0;
    // StateChanged is an event handler other pages
    // can subscribe to 
    public event EventHandler StateChanged;
    // This method will always return the current count
    public int GetCurrentCount()
    {
        return _currentCount;
    }
    // This method will be called to update the current count
    public void SetCurrentCount(int paramCount)
    {
        _currentCount = paramCount;
        StateHasChanged();
    }
    // This method will allow us to reset the current count
    public void ResetCurrentCount()
    {
        _currentCount = 0;
        StateHasChanged();
    }
    private void StateHasChanged()
    {
        // This will update any subscribers
        // that the counter state has changed
        // so they can update themselves
        // and show the current counter value
        StateChanged?.Invoke(this, EventArgs.Empty);
    }
}

你可以在 startup.cs 文件中像这样注册它:

services.AddScoped<CounterState>();

您可以在每个 .razor 控件中使用以下方式引用它:

@inject CounterState CounterState

一个控件可以设置如下的值:

// Call the GetCurrentCount() method
// to get the current count
int CurrentCount = CounterState.GetCurrentCount();
// Increase the count
CurrentCount++;
// Set Current count on the Session State object
CounterState.SetCurrentCount(CurrentCount);

应用程序中的任何位置都可以像这样接收该值的另一个控件:

   // This method is called when the control is initialized
    protected override void OnInitialized()
    {
        // Subscribe to the StateChanged EventHandler
        CounterState.StateChanged +=
        OnCounterStateAdvancedStateChanged;
    }
    // This method is fired when the CounterState object
    // invokes its StateHasChanged() method
    // This will cause this control to invoke its own
    // StateHasChanged() method refreshing the page
    // and displaying the updated counter value
    void OnCounterStateAdvancedStateChanged(
        object sender, EventArgs e) => StateHasChanged();
    void IDisposable.Dispose()
    {
        // When this control is disposed of
        // unsubscribe from the StateChanged EventHandler
        CounterState.StateChanged -=
        OnCounterStateAdvancedStateChanged;
    }

我发现(虽然是Blazor),在Blazor中使用View Models很关键。我处理时序问题时头疼不断,因为事情的发生顺序无法预测或检测。有时您的变量为空,因为它还没有被设置,而其他时候DOM尚未准备好等等... 创建一个具有属性和方法的视图模型,然后从中创建一个接口,然后使用依赖注入添加它。这使您不再关注事物的顺序,并且视图模型的生命周期可以设置为Singleton,Transient或Scoped。 - Tim Davis

3

作为替代方案,您可以使用Rx.Net。

您可以使用类似这样的服务。

public interface IThemeMessageService<T>
{
    void SendMessage(ActionMessage<T> message);

    IObservable<ActionMessage<T>> GetMessage();
}

public class ThemeMessageService<T>: IThemeMessageService<T>
{
    private readonly Subject<ActionMessage<T>> _subject = new Subject<ActionMessage<T>>();

    public void SendMessage(ActionMessage<T> message) => _subject.OnNext(message);

    public IObservable<ActionMessage<T>> GetMessage() => _subject;

}

发送消息:
var actionMessage = new ActionMessage<MyData>
{
    Emitter = ThemeMessageEmitter.Component1,
    Data = data
};

ThemeMessageService.SendMessage(actionMessage);

接收消息:

 ThemeMessageService.GetMessage().Subscribe(p =>
 {

     data= p.Data;

 });

消息类:

public class ActionMessage<T>
{
    public ThemeMessageEmitter Emitter { get; set; }
    public T Data { get; set; }
}

发射器:你可以在这里注册发送数据的组件。
public enum ThemeMessageEmitter
{
    Component1 = 1,
    Component2 = 2,
}

别忘了在启动时注册服务

 services.AddSingleton(typeof(IThemeMessageService<MyData>), typeof(ThemeMessageService<MyData>));

您可以在我的 Blazor 管理主题中查看所有内容的运行情况。 https://github.com/amuste/BlazorAdminDashboard/tree/master/BlazorAdminDashboard.Client/Shared/Theme

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接