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

Posted

技术标签:

【中文标题】如何在同一 Blazor 页面上的 Razor 组件之间传递数据?【英文标题】:How do I pass data between Razor components on the same Blazor page? 【发布时间】:2020-02-18 23:36:54 【问题描述】:

我有这个 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 传递给第二个组件,所以我可以将最后一行中的硬编码86 替换为PositionId 参数。当我尝试发布此内容时出现错误

看起来您的帖子主要是代码;请添加更多详细信息。

我不太确定添加哪些细节可以帮助某人回答这个问题。

【问题讨论】:

【参考方案1】:

是的,您有两个不直接相关的控件,因此您不能只传递一个参数。

两种选择:

级联参数:https://docs.microsoft.com/en-us/aspnet/core/blazor/components?view=aspnetcore-3.0#cascading-values-and-parameters

或状态管理。对于状态管理,这可能会有所帮助: Implementing State Management In 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 的视图模型是关键。我在处理时间问题上遇到了很多麻烦,事情发生的顺序是你无法预测或检测到的。有时你的变量是空的,因为它还没有被设置,有时 DOM 还没有准备好,等等……用你的属性和方法创建一个视图模型,然后从它创建一个接口,然后使用依赖注入添加它。这让您不再关心事物的顺序,视图模型的生命周期可以设置为 Singleton、Transient 或 Scoped。【参考方案2】:

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

此解决方案分为三个步骤:

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

第 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 docs。

希望这会有所帮助。

【讨论】:

我要试试事件回调的方法。我应该提到第二个组件有一个按钮,可以触发从第一个组件检索 id。 谢谢,成功了!我丢弃了 Task OnPositionIdChanged,并从第一个组件的设置器中调用了 PositionIdChanged.InvokeAsync。【参考方案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; 

Emiter:你可以在这里注册发送数据的组件

public enum ThemeMessageEmitter

    Component1 = 1,
    Component2 = 2,

别忘了在 Startup 中注册服务

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

您可以在我的 blazor 管理主题中查看所有操作 https://github.com/amuste/BlazorAdminDashboard/tree/master/BlazorAdminDashboard.Client/Shared/Theme

【讨论】:

以上是关于如何在同一 Blazor 页面上的 Razor 组件之间传递数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Blazor 中创建 Razor 页面列表

如何从 Blazor 服务器端应用程序中的 Razor 页面导航到 Blazor 组件?

如何从服务器端 Blazor 应用程序中的 Blazor 组件调用 razor 页面而不导致页面刷新

如何从 Blazor WebAssembly 中的 Razor 页面访问 body 标签或其他标签?

在 Blazor 应用 Razor 页面的 InputText 中绑定类型双精度值

Blazor 脚手架注册页面为啥 .cshtml 不是 .razor 文件?