Blazor University (21)使用 RenderFragments 模板化组件 —— 传递占位符

原文链接:https://blazor-university.com/templating-components-with-renderfragements/passing-placeholders-to-renderfragments/

将占位符传递给 RenderFragments

源代码[1]

说明:此页面的灵感来自用户 ℳisterℳagoo 的 Twitter 帖子。

首先,声明 RenderFragment<RenderFragment> 类型的 [Parameter] 属性可能看起来不直观,或者可能有点奇怪。

[Parameter]
public RenderFragment<RenderFragment> ChildContent { get; set; }

事实上,如果您曾经创建过自定义 Blazor 布局,那么您已经熟悉了类似的概念。

RenderFragment<T> 中的 <T> 作为 @context 变量传递给用户指定的标记。布局使用名称 @Body 而不是 @context,但 @Body 实际上是一个 RenderFragment。为了证明这一点,编辑 /Shared/MainLayout.razor 文件并将 @Body 更改为以下内容。

@Body.GetType().Name

现在我们将看到类名,而不是显示 @Body 渲染的内容,它恰好是 RenderFragment

虽然这并不是 Blazor 布局的工作原理,但它是一个有用的比较,有助于理解声明 RenderFragment<RenderFragment> 类型的 [Parameter] 属性背后的原理。

<div class="our-main-layout">@Body
</div>

在前面的虚构布局中,我们可以将整个标记想象为某个父组件的 ChildContent RenderFragment,而第 3 行的 @Body 相当于 @context(命名为 Body),我们可以选择在我们希望的任何地方注入。在本示例中,我们选择将 Body 注入 HTML <div> 元素中。。

对应的等价代码

<OurComponent><p>@context</p>
</OurComponent>

或者,如果我们想使用名称 Body(或任何其他名称) 而不是上下文,我们可以指定用于上下文的名称。请参阅将数据传递给 RenderFragment[2] 末尾的避免 @context 名称冲突部分。

<OurComponent Context="FragmentWeNeedToRender"><div class="our-wrapped-fragment">@FragmentWeNeedToRender</div>
</OurComponent>

创建一个工作示例

首先,创建一个我们可以用来绑定一些数据的类。

public class Person
{public string Salutation { get; set; }public string GivenName { get; set; }public string FamilyName { get; set; }
}

创建一个简单的模板化重复组件

此详细信息类似于使用 @typeparam 创建通用组件[3]一节中介绍的内容。

接下来,我们需要在 /Shared 中创建一个名为 DataList.razor 的新组件。该组件将是一个通用组件(使用 @typeparam),并将采用一个 IEnumerable<TItem> 并迭代可枚举以使用其使用者指定的模板呈现每个项目的内容。

@typeparam TItem
<ul>@foreach (TItem item in Data ?? Array.Empty<TItem>()){@ItemTemplate(item)}
</ul>
@code
{[Parameter]public IEnumerable<TItem> Data { get; set; }[Parameter]public RenderFragment<TItem> ItemTemplate { get; set; }
}

这个组件可能会被我们的 Index 页面使用,如下所示:

@page "/"<DataList Data=@Data><ItemTemplate><li @key=context>@context.Salutation @context.GivenName @context.FamilyName</li></ItemTemplate>
</DataList>@code
{private IEnumerable<Person> People;protected override void OnInitialized(){base.OnInitialized();People = new Person[]{new Person { Salutation = "Mr", GivenName = "Bob", FamilyName = "Geldof" },new Person { Salutation = "Mrs", GivenName = "Angela", FamilyName = "Rippon" },new Person { Salutation = "Mr", GivenName = "Freddie", FamilyName = "Mercury" }};}
}

注意: <li> 元素中的 @key=context 用于优化性能,应在呈现列表时使用。有关更多信息,请参阅使用 @key 进行优化[4]

问题

如果我们的 DataList 组件不仅输出项目列表怎么办。也许它有一个页脚,允许用户使用“上一个”/“下一个”按钮一次显示一页元素,还有一个页脚显示总共有多少项目?

我们可以简单地将这个额外的标记添加到我们的 DataList 组件中,但是如果我们的组件的使用者也想要以不同的方式呈现列表怎么办?也许他们需要在一个地方将分页列表显示为 HTML <table>,而在其他地方显示 HTML <ul>

<div class="paged-data-list"><div class="paged-data-list_header">@Data.Count() item(s)</div><div class="paged-data-list_body"><!-- Consumer wants either a <table> or a <ul> here -->@foreach(TItem item in CurrentPageOfData){@ItemTemplate(item)}<!-- Consumer wants either a </table> or a </ul> here --></div><div class="paged-data-list_footer"><button type="button" etc>Prev</button><button type="button" etc>Next</button> </div>
</div>

这正是需要我们让用户传入 RenderFragment<RenderFragment> 的原因。

在 RenderFragment 中渲染 RenderFragment

现在我们的 ItemTemplate 接收了 Person 类型的 @context 以呈现每个元素,我们需要允许我们的组件的使用者指定在第一个元素之前和最后一个元素之后要包含的内容 - 如前面的代码中突出显示的那样第 6 行和第 11 行的示例。

组件使用代码看起来类似于以下两个示例中的任何一个

<DataList Data=@People><ItemTemplate Context="person"><li @key=person>@person.Salutation @person.FamilyName, @person.GivenName</li></ItemTeplate>
</DataList><DataList Data=@People><ListTemplate Context="allPeople"><ul Type="A">@allPeople</ul></ListTemplate><ItemTemplate Context="person"><li @key=person>@person.Salutation @person.FamilyName, @person.GivenName</li></ItemTemplate>
</DataList>
  • 第 2 行

    定义要用于列表的模板。在这种情况下,我们用 <ul></ul> 包装我们的内部内容(我们的数据项“allPeople”)。

  • 第 4 行

    执行通过我们选择称为“allPeople”的上下文变量传递的 RenderFragment 来指示在何处渲染项目标记。

  • 第 7 行

    为每个项目指定一个模板。在这种情况下,上下文将是 Person 的一个实例,因此我们选择通过指定 Context="person" 以名称“person”来引用上下文。

将渲染的内容作为占位符传递给消费者显示

DataList 组件需要呈现 Data 属性中的每个项目,然后将其传递给使用者以决定在其 ListTemplate 中的哪个位置呈现该输出。或者,更准确地说,DataList 需要传递一个 RenderFragment,它会在执行时呈现项目的标记。

首先,我们将 ListTemplate 属性添加到我们的 DataList 组件中。

[Parameter]
public RenderFragment<RenderFragment> ListTemplate { get; set; }

接下来,我们将更改我们的 DataList,以便当消费者没有指定 ListTemplate 时它使用 <ul><li> 作为默认值,之后我们将处理组件使用者确实想要使用自定义 ListTemplate 的场景.

使用默认列表模板进行渲染

这是简单的部分。我们需要做的就是编写我们的标记,就好像组件上没有 ListTemplate 这样的东西,就像我们通常那样——但前提是 ListTemplate 属性为空。

@typeparam TItem
@if (ListTemplate == null)
{<ul>@foreach (TItem item in Data ?? Array.Empty<TItem>()){@ItemTemplate(item)}</ul>
}@code
{[Parameter]public IEnumerable<TItem> Data { get; set; }[Parameter]public RenderFragment<TItem> ItemTemplate { get; set; }[Parameter]public RenderFragment<RenderFragment> ListTemplate { get; set; }
}
  • 第 2 行

    检查 ListTemplate 是否为空

  • 第 4-9 行

    如果 ListTemplate 为 null,那么我们渲染一个标准的 <ul> 列表,然后使用 ItemTemplate 渲染 Data 中的元素。

使用自定义 ListTemplate 进行渲染

RenderFragment<TItem> 期望我们在每次渲染它时传递一个 TItem 实例。这很简单,因为我们有一个 IEnumerable<TItem> ,我们可以从中提取要渲染的值,但是当我们需要将 RenderFragment 的实例传递给我们的模板时该怎么办?

要定义非泛型 RenderFragment,我们可以使用标准 Razor 转义序列来表示 HTML,即@

RenderFragment rf = @<h1>Hello</h1>;

要定义 RenderFragment<T>,我们需要使用传入 T 实例的 lambda 表达式

RenderFragment<Person> rf = person => @<h1>Hello @person.Name</h1>;

那么我们如何返回一个 RenderFragment,它只会在渲染时循环遍历 Data 属性中的项目?为此,我们需要使用 wig-pig 语法。

wig-pig

wig-pig 是 Razor 渲染引擎可用于表示 C# 文件中的 Razor 标记块的一组字符。出于显而易见的原因,此字符序列仅适用于 .razor 文件。如有雷同,纯属巧合。

注意: @:@{ 实际上是两个字符序列。第一个 @: 告诉 Razor 解析器将以下文本视为 Razor 标记,然后 @{ 是 C# 代码块的开始——它显然会在某个地方以补充 } 结束。最终,这给了我们一大块 Razor 标记,它相当于一个带有 C# 代码的 RenderFragment,它可以执行诸如循环之类的操作。

在 Razor 文件中调用 @ListItem(...) 时,我们可以使用 wig-pig 语法将 RenderFragment 作为参数传递。

@ListTemplate(@:@{foreach (TItem item in Data ?? Array.Empty<TItem>()){@item.ToString()}}
)

使用这种语法,我们渲染组件使用者在 <ListTemplate> 中指定的标记,并传入一个 RenderFragment,该 RenderFragment 将渲染 Data 中的所有元素。在前面的代码中,它只是对 Data 中的每个项目调用 ToString()。但是,理想情况下,我们希望组件使用者为我们提供的 ItemTemplate

@ListTemplate(@:@{foreach (TItem item in Data ?? Array.Empty<TItem>()){@ItemTemplate(item)}}
)

DataList.razor

@typeparam TItem
@if (ListTemplate == null)
{<ul>@foreach (TItem item in Data ?? Array.Empty<TItem>()){@ItemTemplate(item)}</ul>
}
else
{@ListTemplate(@:@{foreach (TItem item in Data ?? Array.Empty<TItem>()){@ItemTemplate(item)}})
}@code
{[Parameter]public IEnumerable<TItem> Data { get; set; }[Parameter]public RenderFragment<TItem> ItemTemplate { get; set; }[Parameter]public RenderFragment<RenderFragment> ListTemplate { get; set; }
}

使用我们的 DataList

示例 1:一个简单的列表

<DataList Data=@People><ItemTemplate><li @key=context>@context.Salutation @context.GivenName @context.FamilyName</li></ItemTemplate>
</DataList>

生成以下 HTML

<ul><li>Mr Bob Geldof</li><li>Mrs Angela Rippon</li><li>Mr Freddie Mercury</li>
</ul>

bbdde780ff1b8d2b97f39a093be0dc8c.png

示例 2:一个 HTML 表格

<DataList Data=@People><ListTemplate Context="listOfPeople"><table border=1 cellpadding=4><thead><tr><th>Salutation</th><th>Given name</th><th>Family name</th></tr></thead><tbody>@listOfPeople</tbody></table></ListTemplate><ItemTemplate Context="person"><tr @key=@person><td>@person.Salutation</td><td>@person.GivenName</td><td>@person.FamilyName</td></tr></ItemTemplate>
</DataList>

生成以下 HTML

<table cellpadding="4" border="1"><thead><tr><th>Salutation</th><th>Given name</th><th>Family name</th></tr></thead><tbody><tr><td>Mr</td><td>Bob</td><td>Geldof</td></tr><tr><td>Mrs</td><td>Angela</td><td>Rippon</td></tr><tr><td>Mr</td><td>Freddie</td><td>Mercury</td></tr></tbody>
</table>

780ed50b713b259c11a14819c9df04fe.png

未指定 RenderFragments 时分配默认值

目前,我们必须在视图中使用 @if 语句检查 ListTemplate 是否为 null,我们甚至没有检查 ItemTemplate 是否已设置。这些方法都不是理想的。

相反,如果组件使用者没有设置我们的 RenderFragment 属性,我们应该将它们设置为所需的默认值。这样我们组件的渲染逻辑就可以变得更加简单。

@ListTemplate(@:@{foreach(TItem item in CurrentPage){@ItemTemplate(item)}}
)

在我们的组件中重写 OnParametersSet() 并确保 ItemTemplate 属性不为空。为此,我们创建一个接收 TItem 并返回 RenderFragment 的 lambda(使用 wig-pig 语法)。

protected override void OnParametersSet(){if (ItemTemplate == null){ItemTemplate = (item) => @:@{ <li @key=item>@item.ToString()</li>};}
}

为了确保 ListTemplate 不为空,我们创建了一个接收 RenderFragment 的 lamba,并返回我们自定义的 wig-pig RenderFragment

if (ListTemplate == null){ListTemplate = _ => @:@{ <ul>@foreach(TItem item in CurrentPage){@ItemTemplate(item)}</ul>};}

注意: ;不能与前面的 } 在同一行,否则 Razor 解析器无法正确解析源。这已被报告为一个错误,并有望在不久的将来得到修复。

总结

当组件使用者希望在其标记中为将在渲染期间传递给他们的内容识别占位符时,应使用 RenderFragment<RenderFragment> 技术 - 例如 Blazor 布局中的 Body 占位符。

希望本节可以帮助您了解何时使用此技术,以及如何使用 wig-pig @:@{ 语法来就地生成 RenderFragments

补充阅读

本节的源代码包括一个使用 `PagedDataList` 组件[5]的附加示例页面。

参考资料

[1]

源代码: https://github.com/mrpmorris/blazor-university/tree/master/src/TemplatedComponents/PassingPlaceholdersToRenderFragments

[5]

使用 PagedDataList 组件: https://github.com/mrpmorris/blazor-university/blob/master/src/TemplatedComponents/PassingPlaceholdersToRenderFragments/Shared/PagedDataList.razor

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287550.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

物联网(车联网)平台架构方案

技术支持QQ&#xff1a;787728951、车载终端网关采用mina/nettyspring架构&#xff0c;独立于其他应用&#xff0c;主要负责维护接入终端的tcp链接、上行以及下行消息的解码、编码、流量控制&#xff0c;黑白名单等安全控制&#xff0c;网关同时支持交通部JT/T808-2011、JT/T80…

[python opencv 计算机视觉零基础到实战] 八、ROI泛洪填充

一、学习目标 了解什么是ROI了解floodFill的使用方法 如有错误欢迎指出~ 目录 [python opencv 计算机视觉零基础到实战] 一、opencv的helloworld [【python opencv 计算机视觉零基础到实战】二、 opencv文件格式与摄像头读取] 一、opencv的helloworld [[python opencv 计…

解决冲突

人生不如意之事十之八九&#xff0c;合并分支往往也不是一帆风顺的。 准备新的feature1分支&#xff0c;继续我们的新分支开发&#xff1a; $ git checkout -b feature1 Switched to a new branch feature1修改readme.txt最后一行&#xff0c;改为&#xff1a; Creating a new …

HQL入门学习

2019独角兽企业重金招聘Python工程师标准>>> package myHibernate; /** 测试简单的HQL语句* 2010年4月9日 23:36:54* */ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.uti…

Oracle精简客户端配置

2019独角兽企业重金招聘Python工程师标准>>> 由于Oracle client体积很大。而且安装后&#xff0c;基本上就用2个功能&#xff1a;TNS配置服务名和SQL*Plus。下面是一种小巧、快捷的Oracle客户端配置方法&#xff1a; 1.下载Instant Client 下载地址&#xff1a; htt…

WinUI迁移到.NET MAUI个人体验

迁移的初衷本人平时是做.net相关的工作&#xff0c;对于.net技术栈也有一些了解&#xff0c;自从新的.net能够跨平台之后&#xff0c;之前也有跨平台的ui框架Xamarin&#xff0c;现在微软推出了.NET MAUI这个说是 统一了开发体验&#xff0c;而且都RC版本了&#xff0c;所以本人…

祝CSDN2021牛气冲天祝我也拨云散雾

前言 2020年4月&#xff0c;我写了一篇用turtle绘制《小清新风格的树》&#xff0c;反响挺好。现在打算使用turtle修改一下绘制方式&#xff0c;因为线条的绘制太过考虑因素过多&#xff0c;如果使用方块进行堆叠&#xff0c;绘制出来的形状可以如马赛克一样&#xff0c;既符合…

FPGA图案--数字表示(代码+波形)

在数字逻辑系统&#xff0c;仅仅存在高低。所以用它只代表一个整数数字。并且有3代表性的种类。这是&#xff1a;原码表示(符号加绝对值值)、反码表示(加-minus标志)而补码(符号加补)。这三个在FPGA中都有着广泛的应用。以下分别讨论。1、原码表示法 原码表示法是机器数的一种简…

WPF效果第一百八十四篇之网页视频保存

一年一度的小学入学采集开始了;我一朋友很是头大,他说头都大了好几圈了;既要准备各种入学材料又要听线上专人视频直播讲解;然而在直播结束后,他发现自己仍是一脸疑惑;虽说直播有回访吧,但是他那蜗牛网速简直了;这时他场外找我,让我看能不能给他自己下载一份;1、毕竟第一次,直接…

【遥感数字图像处理】基础知识:第一章 绪论

第一章 绪 论 ◆ 课程学习要求 主要教学内容&#xff1a;遥感数字图像处理的概念和基础知识&#xff0c;遥感数字图像的几何处理&#xff0c;遥感图像的辐射校正&#xff0c;遥感数字图像的增强处理&#xff0c;遥感图像的计算机分类&#xff0c;遥感数字图像的分析方法&…

自定义Git

在安装Git一节中&#xff0c;我们已经配置了user.name和user.email&#xff0c;实际上&#xff0c;Git还有很多可配置项。 比如&#xff0c;让Git显示颜色&#xff0c;会让命令输出看起来更醒目&#xff1a; $ git config --global color.ui true这样&#xff0c;Git会适当地显…

[python opencv 计算机视觉零基础到实战] 九、模糊

一、学习目标 了解什么是卷积了解模糊的使用方法与应用 如有错误欢迎指出~ 二、了解模糊的应用 上一篇:[python opencv 计算机视觉零基础到实战] 八、ROI泛洪填充 2.1 了解卷积是什么 在本节中&#xff0c;卷积我们不过多的进行深入讲解&#xff0c;我本人对卷积也只是稍…

windbg的时间旅行实现对 C# 程序的终极调试!

一&#xff1a;什么是时间旅行 简而言之就是把程序的执行流拍成vlog&#xff0c;这样就可以对 vlog 快进或者倒退&#xff0c;还可以分享给别人做进一步的分析&#xff0c;是不是想都不敢想。很开心的是 windbg preview 版本中已经实现了&#xff0c;叫做 时间旅行调试 TTD&…

【神经网络】神经网络结构在命名实体识别(NER)中的应用

命名实体识别&#xff08;Named Entity Recognition&#xff0c;NER&#xff09;就是从一段自然语言文本中找出相关实体&#xff0c;并标注出其位置以及类型&#xff0c;如下图。它是NLP领域中一些复杂任务&#xff08;例如关系抽取&#xff0c;信息检索等&#xff09;的基础。…

[python opencv 计算机视觉零基础到实战] 十、图片效果毛玻璃

一、学习目标 了解高斯模糊的使用方法了解毛玻璃的图片效果添加了解如何自己做一个噪声图片 上一篇:[python opencv 计算机视觉零基础到实战] 九、模糊 如有错误欢迎指出~ 二、了解模糊与美颜 2.1 使用高斯模糊降噪 由于很多小伙伴反应抛开原理或理论讲解使用用法对于初学…

Android之自定义View实现带4圆角或者2圆角的效果

1 问题 实现任意view经过自定义带4圆角或者2圆角的效果 2 原理 1) 实现view 4圆角 我们只需要把左边的图嵌入到右边里面去,最终显示左边的图就行。 2) 实现view上2圆角 我们只需要把左边的图嵌入到右边里面去,最终显示左边的图就行。 安卓源码里面有这样的类 package and…

java trim()函数_Java - split()函数和trim()函数的使用方法

split()函数和trim()函数的使用方法本文地址: http://blog.csdn.net/caroline_wendy/article/details/24465141详细參考Java API: http://docs.oracle.com/javase/6/docs/api/java/lang/String.htmlsplit()函数是依据參数如",", "-", " "等, 切割…

分布式服务器集群架构方案思考

0x01.大型网站演化 简单说&#xff0c;分布式是以缩短单个任务的执行时间来提升效率的&#xff0c;而集群则是通过提高单位时间内执行的任务数来提升效率。 集群主要分为&#xff1a;高可用集群(High Availability Cluster)&#xff0c;负载均衡集群(Load Balance Cluster&…