如何绑定到使用组件标签助手呈现的组件的值

How to bind to value of component rendered with the component tag helper

我需要在预先存在的视图和页面上使用一些构建为 razor 组件的自定义输入,但似乎无法使用组件标签助手让它工作。例如,我一直在测试的组件代码(来自 https://chrissainty.com/creating-bespoke-input-components-for-blazor-from-scratch/)首先导致异常,因为 ValueExpression 最终为空(据我所知,没有使用标签助手进行绑定的选项)。如果我然后自己设置 ValueExpression,我最终会遇到 json 异常(检测到对象循环)。我想可能是因为将参数从标签助手移动到底层组件的机制不支持 Func<> 对象?不确定。

我是不是错误地使用了标签助手?我在其他地方使用它来呈现独立的组件(比如整个 EditForm),这似乎工作正常,但我不知道如何让它在这个特定的用例中工作:(

我希望控件呈现在 .cshtml 文件中:

<component type="typeof(MyComponent)" render-mode="ServerPrerendered" param-ValueExpression="(Func<string>)(() => LocalProperty)" />

MyComponent.razor

<input class="_fieldCssClasses" value="@Value" @oninput="HandleInput" />

@if (_showValidation) {
    <div class="validation-message">You must provide a name</div>
}

@code {
    private FieldIdentifier _fieldIdentifier;
    private string _fieldCssClasses => EditContext?.FieldCssClass(_fieldIdentifier) ?? "";
    private bool _showValidation = false;

    [CascadingParameter] private EditContext EditContext { get; set; }

    [Parameter] public string Value { get; set; }
    [Parameter] public EventCallback<string> ValueChanged { get; set; }
    [Parameter] public Expression<Func<string>> ValueExpression { get; set; }
    [Parameter] public bool Required { get; set; }

    protected override void OnInitialized() {
        _fieldIdentifier = FieldIdentifier.Create(ValueExpression);
    }

    private async Task HandleInput(ChangeEventArgs args) {
        await ValueChanged.InvokeAsync(args.Value.ToString());

        if (EditContext != null) {
            EditContext.NotifyFieldChanged(_fieldIdentifier);
        } else if (Required) {
            _showValidation = string.IsNullOrWhiteSpace(args.Value.ToString());
        }
    }
}

所以为了解决这个问题,我刚刚围绕内部组件制作了一个特定于用例的包装器组件,确保还传入一个 Id,内部组件将分配给输入的 name 和 id 属性呈现(允许它在您提交表单时“绑定”),然后在 page/view 上包含包装器组件。包装器只是满足内部组件的依赖关系,如果依赖关系更复杂(如 Func<> 对象),组件标签助手不提供机制。

MyComponentWrapper.razor:

<div class='purdy'>
    <MyComponent Id="@Id" @bind-Value="Value"></MyComponent>
</div>
...
@code {
    [Parameter]
    public string Id { get; set; }
    [Parameter]
    public string Value { get; set; }
    ...
}

MyContainingPage.cshtml:

<component type="typeof(MyComponentWrapper)" render-mode="ServerPrerendered" param-Id="myFormInput" param-Value="LocalProperty" />
...