在Blazor中将子组件作为参数传递

9
我希望在Blazor中做一些我通常在React中做的事情:创建一个可重用组件,该组件内部使用其他子组件,并能够将这些子组件作为参数传递。我需要将子组件视为可以按需注入任何自定义实现的依赖项,以便在不同上下文中使用。
例如,想象一个TextBox.razor组件,它使您能够传递一个自定义组件来呈现标签,只要该组件实现了ILabel接口即可。我尝试了类似于这样的东西,但是语法似乎无效:

TextBox.razor

enter image description here

从截图中可以看到,Blazor不允许我将参数Label用作组件。有什么办法可以实现这个功能吗?


@PeterMorris 一个实例 - tocqueville
渲染片段是否能够满足你的需求? - Peter Morris
并不是真的...我所想的是一种组件的“依赖注入”。我使用接口编写代码,然后可以决定交换实现。 - tocqueville
我在 GitHub 上开了一个问题:https://github.com/aspnet/AspNetCore/issues/17502 - tocqueville
你肯定可以注册一个抽象工厂? - Peter Morris
显示剩余3条评论
2个回答

10
你应该可以通过使用模板组件来完成这个任务。
Textbox.razor
@typeparam inputType
<div class="textbox">
    @if(LabelTemplate!=null && TItem!=null)
        @LabelTemplate(TItem)
    <input type="text"/>
</div>


    @code{
        [Parameter]
        public RenderFragment<inputType> LabelTemplate { get; set; }
        [Parameter]
        public inputType TItem { get; set; }
    }

在上面的代码中,您正在指定组件接受类型,使用@typeparam inputType,并接受该类型的对象作为参数TItem
您还接受一个LabelTemplate,该模板接受类型为inputType的对象。为了呈现此片段,我们调用@LabelTemplate并传入我们的TItem参数。
现在让我们看看如何在名为PersonForm.razor的新组件中使用我们的模板化组件。
<Textbox TItem="myPerson">
    <LabelTemplate>
        @context.Name
    </LabelTemplate>
</Textbox>
<Textbox TItem="myPerson">
    <LabelTemplate>
        @context.PhoneNumber
    </LabelTemplate>
</Textbox>

@code{

  Person myPerson = new Person { Name = "Jane Doe", PhoneNumber = "999 999 9999" };
    public class Person
    {
        public string Name { get; set; }
        public string PhoneNumber { get; set; }
    }
}


我正在将我的Person对象传递给每个Textbox组件的TItem属性,并使用@context语法在LabelTemplate中访问它。这一开始可能会很困惑,因此请在此处详细了解:这里 编辑: 这只是取决于你想要实现什么。使用冗长的语法可以在组件的“实现”方面提供灵活性。您不必强制进行可能无法与各种模型/类一起使用的接口,而是让实现指定要执行的操作。
如果您想要更少冗长/更严格的内容,也可以执行以下操作。
@implements ILabel 
<div class="textbox"> 
    <label>@Text</label> 
    <input type="text"/> 
</div> 
@code
{ 
    [Parameter] 
    public string Text { get; set; } 
} 

ILabel.cs

    public interface ILabel
    {
        string Text { get; set; }
    }


3
我猜这是一种实现类似结果的方式,但不完全是我要找的。与仅通过参数传递组件类型相比,这种方式感觉非常冗长。 - tocqueville
2
也许我误解了你最初的意图。请查看我回答的编辑部分。 - Eric Holland

6

我知道可能已经晚了,但是我刚刚克服了这个问题,发现它非常容易解决!想为正在寻找答案的人提供一个简单的答案。

这是我的OrdersNavigation.razor文件(我想将其嵌入到标题中):

<div class="nav-strip">
    <NavLink href="orders">
        <Icon Name="@Icons.Cart" /> List
    </NavLink>
    <NavLink href="orders/create">
        <Icon Name="@Icons.Plus" /> Create
    </NavLink>
</div>

现在这是我的PageHeader.razor:
<div class="page-header">
    <h3>@Title</h3>
    @Navigation
</h3>
<hr />

@code {
    [Parameter] public string Title { get; set; } = "[TITLE]";
    [Parameter] public RenderFragment Navigation { get; set; }
}

请注意,导航属性是一个RenderFragment - 这非常关键。现在在我的页面中,我可以像这样简单地添加它:

<PageHeader Title="Orders">
    <Navigation>
        <OrderNavigation />
    </Navigation>
</PageHeader>

在这里,您可以看到像往常一样输入了Title参数,但是Navigation参数是作为PageHeader的一个元素来输入的!实际上,您可以将任何内容放在标签中,并且它会在您使用@ Navigation的地方呈现。

参考:https://blazor-university.com/templating-components-with-renderfragements/passing-data-to-a-renderfragement/

对您的示例进行了尝试:

Label.razor

<label>@Text</label>

@code {
    [Parameter] public RenderFragment Text { get; set; }
}

TextBox.razor

<div class="textbox">
    <Label>
        <Text>
            <div>
                Embedded label <br />
                You can even drop components in here!
            </div>
        </Text>
    </Label>
    <input  />
</div>

在你的例子中,你只是渲染传递为RenderFragment的任何组件。在我的例子中,我定义了一个精确的接口“ILabel”,它作为可以使用的实现的约束。 - tocqueville

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