[转]9.2.3 .net core 通过TagHelper封装控件
本文转自:https://www.cnblogs.com/BenDan2002/p/6170624.html
.net core 除了继续保留.net framework的HtmlHelper的写法以外,还提供了TagHelper和ViewComponent方式生成控件。
我们本节说的是使用TagHelper来生成控件。不过严格的说起来,TagHelper是对客户端html元素的辅助类,例如渲染、增加服务端特性等。我们可以使用 taghelper 定义自己的标签或更改已知标签。使用TagHelper,VS.net编译环境也可以自动感知,并提供智能提示。因为TagHelper生成的控件,看起来像一个原生的HTML标签一样,也更利于美工进行页面美化。
例如一个lable控件 <label asp-for="Email"></label>,生成的最终html就是这样:<label for="Email">xxx@xx.com</label>
如果要使用TagHelper,除了在页面中using 命名空间之外,还需要使用@addTagHelper来使TagHelper可用。由于我们会编写不止一个TagHelper,且在多个cshtml页面使用,因此我们将如下代码
@using MicroStrutLibrary.Presentation.Web.Controls
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MicroStrutLibrary.Presentation.Web.Controls
写在视图文件 Views/_ViewImports.cshtml中。写在Views/_ViewImports.cshtml的意思是,默认所有的在 Views 和Views下级目录中的视图文件都可以使用TagHelper,通配符 ("*") 来指定在特定程序集(Microsoft.AspNetCore.Mvc.TagHelpers和MicroStrutLibrary.Presentation.Web.Controls)中的所有的 TagHelpers 在 Views目录和子目录中的视图文件中都是可用的。第一个程序集是mvc自带的,第二个是我们自己的控件程序集。具体的说明大家还是看.net core文档吧。
下面,我们仍旧以上一节介绍的多选控件MultiSelect为例,说明下我们的TagHelper封装过程。
MultiSelect在cshtml中的写法如下,asp-dataSource传入的是数据源,select的所有下拉内容都是通过datasource生成的,而asp-value传入的是选中的项:
1 @model Dictionary<string, string>
2 @{
3 List<string> values = ViewData.Get<List<string>>("Values");
4 }
5
6 <multiSelect id="txtInput0" asp-dataSource="@Model" asp-value="@values"></multiSelect>
Model和values从controller中传过来的,具体如下:
1 public IActionResult MultiSelect()
2 {
3 Dictionary<string, string> data = new Dictionary<string, string>();
4 data.Add("1", "Aaaaa");
5 data.Add("2", "Aaron");
6 data.Add("3", "Abbott");
7 data.Add("4", "Abel");
8 data.Add("5", "Abner");
9 data.Add("6", "Abraham");
10 data.Add("7", "Adair");
11 data.Add("8", "Adam");
12 data.Add("9", "Addison");
13
14 List<string> value = new List<string>();
15 value.Add("2");
16 value.Add("7");
17 ViewData["Values"] = value;
18
19 return View(data);
20 }
这样,最终生成的html代码和页面如下:
1 <div>
2 <select id="txtInput0" name="txtInput0" multiple="multiple">
3 <option value="1" >Aaaaa</option>
4 <option value="2" selected>Aaron</option>
5 <option value="3" >Abbott</option>
6 <option value="4" >Abel</option>
7 <option value="5" >Abner</option>
8 <option value="6" >Abraham</option>
9 <option value="7" selected>Adair</option>
10 <option value="8" >Adam</option>
11 <option value="9" >Addison</option>
12 </select>
13 <script>
14 require(['jquery', 'bootstrap', 'multiselect'], function ($) {
15 $(function(){
16 $("#txtInput0").multiselect({
17 nonSelectedText: '请选择',
18 enableFiltering: true,//是否显示过滤
19 filterPlaceholder: '查找',
20 includeSelectAllOption: true,//是否显示全选
21 selectAllText: '全选',
22 numberDisplayed: 5//显示条数
23 });
24 });
25 });
26 </script>
27 </div>
MultiSelect在页面展示如下图
按照设计,MultiSelect应该能够传入:1、数据源,也就是下拉列表的所有数据;2、已选项,已选择的数据;3、下拉列表的展示条数,以免条数过多,影响页面;4、是否只读等属性,更好的控制控件的展示内容。当然了,还应该包括一个For属性,这个For属性的意思是通过页面的Model绑定,自动生成控件Id和已选择数据项等内容。
由于所有的TagHelper都继承于一个基类TagHelper,我们首先看下TagHelper抽象类:
1 //
2 // 摘要:
3 // Class used to filter matching HTML elements.
4 public abstract class TagHelper : ITagHelper
5 {
6 protected TagHelper();
7
8 //
9 // 备注:
10 // Default order is 0.
11 public virtual int Order { get; }
12
13 //
14 public virtual void Init(TagHelperContext context);
15 //
16 // 摘要:
17 // Synchronously executes the Microsoft.AspNetCore.Razor.TagHelpers.TagHelper with
18 // the given context and output.
19 //
20 // 参数:
21 // context:
22 // Contains information associated with the current HTML tag.
23 //
24 // output:
25 // A stateful HTML element used to generate an HTML tag.
26 public virtual void Process(TagHelperContext context, TagHelperOutput output);
27 //
28 // 摘要:
29 // Asynchronously executes the Microsoft.AspNetCore.Razor.TagHelpers.TagHelper with
30 // the given context and output.
31 //
32 // 参数:
33 // context:
34 // Contains information associated with the current HTML tag.
35 //
36 // output:
37 // A stateful HTML element used to generate an HTML tag.
38 //
39 // 返回结果:
40 // A System.Threading.Tasks.Task that on completion updates the output.
41 //
42 // 备注:
43 // By default this calls into Microsoft.AspNetCore.Razor.TagHelpers.TagHelper.Process(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext,Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput).
44 public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
45 }
这里最重要的就是Process方法和ProcessAsync方法。这两个方法,一个是同步,一个是异步,作用都是输出页面html代码。
我们再来看封装后的MultiSelectTagHelper的代码:
1 [HtmlTargetElement("multiSelect", Attributes = ForAttributeName)]
2 [HtmlTargetElement("multiSelect", Attributes = ValueAttributeName)]
3 [HtmlTargetElement("multiSelect", Attributes = ShowItemCountAttributeName)]
4 [HtmlTargetElement("multiSelect", Attributes = DataSourceAttributeName)]
5 [HtmlTargetElement("multiSelect", Attributes = ReadonlyAttributeName)]
6 public class MultiSelectTagHelper : TagHelper
7 {
8 private readonly IHtmlGenerator generator;
9
10 public MultiSelectTagHelper(IHtmlGenerator generator)
11 {
12 this.generator = generator;
13 }
14
15 private const string ForAttributeName = "asp-for";
16 private const string ValueAttributeName = "asp-value";
17 private const string ShowItemCountAttributeName = "asp-showItemCount";
18 private const string DataSourceAttributeName = "asp-dataSource";
19 private const string ReadonlyAttributeName = "asp-readonly";
20
21 [HtmlAttributeNotBound]
22 [ViewContext]
23 public ViewContext ViewContext { get; set; }
24
25 [HtmlAttributeName(ForAttributeName)]
26 public ModelExpression For { get; set; }
27
28 [HtmlAttributeName(ValueAttributeName)]
29 public List<string> Value { get; set; }
30
31 [HtmlAttributeName(ShowItemCountAttributeName)]
32 public int ShowItemCount { get; set; } = 5;
33
34 [HtmlAttributeName(DataSourceAttributeName)]
35 public Dictionary<string, string> DataSource { get; set; }
36
37 [HtmlAttributeName(ReadonlyAttributeName)]
38 public bool Readonly { get; set; }
39
40 public override void Process(TagHelperContext context, TagHelperOutput output)
41 {
42 MicroStrutLibraryExceptionHelper.IsNull(context, this.GetType().FullName, LogLevel.Error, "context参数值为空");
43 MicroStrutLibraryExceptionHelper.IsNull(output, this.GetType().FullName, LogLevel.Error, "output");
44
45 output.TagName = "div";
46 //output.Attributes.Add("class", "multiselect-drop");
47
48 MultiSelectList selectList = new MultiSelectList(this.DataSource, "Key", "Value", this.Value);
49
50 HtmlContentBuilder builder = new HtmlContentBuilder();
51
52 string id;
53 if (For == null)
54 {
55 id = output.Attributes["id"].Value.ToString();
56 output.Attributes.Remove(output.Attributes["id"]);
57
58 string options = string.Empty;
59 foreach (SelectListItem item in selectList)
60 {
61 options += $"<option value=\"{item.Value}\" {(item.Selected ? "selected" : "")}>{item.Text}</option>";
62 }
63
64 builder.AppendHtml($"<select id=\"{id}\" name=\"{id}\" multiple=\"multiple\">{options}</select>");
65 }
66 else
67 {
68 id = For.Name;