ASP.NET MVC 6中的自定义标签

Taghelpers是MVC 6中引入的新功能之一,ASP.NET 5的一部分。它们用于生成需要某种服务器端处理的可重复使用的UI。我仔细看看我的上一篇文章中的标签的内置收集, 在ASP.NET MVC 6中介绍标记表格。本文展示了如何创建自己的自定义标签。它将说明两种方法:通过解析自定义属性;并通过在Taghelper上绑定属性。

笔记:本文是针对ASP.NET 5的Beta-4编写的,该版本是Visual Studio 2015 RC的一部分提供的版本。我将努力在RTM之前将文章与未来版本一起更新。

自定义属性

第一个示例具有生成分页链接的Taghelper:

<寻呼机 current-page="1" total-pages="6" link-url="~/Home/Contact/"></寻呼机>

Taghelper的代码在下面的整体上出现,然后解释它是如何工作的:

using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System.Text;

namespace taghelpertest..Helpers
{
    [TargetElement("pager", Attributes = "total-pages, current-page, link-url")]
    public class 寻呼机TagHelper : Taghelper.
    {
        public override void Process(Taghelper.Context context, Taghelper.Output output)
        {
            int totalPages, currentPage;
            if (int.TryParse(context.AllAttributes["total-pages"].ToString(), out totalPages) &&
               int.TryParse(context.AllAttributes["current-page"].ToString(), out currentPage))
            {
                var url = context.AllAttributes["link-url"];
                output.TagName = "div";
                output.PreContent.SetContent("<ul class=\"link-list\">");

                var items = new StringBuilder();
                for (var i = 1; i <= totalPages; i++)
                {
                    var li = new TagBuilder("li");

                    var a = new TagBuilder("a");
                    a.MergeAttribute("href", $"{url}?page={i}");
                    a.MergeAttribute("title", $"Click to go to page {i}");
                    a.InnerHtml = i.ToString();
                    if (i == currentPage)
                    {
                        a.AddCssClass("active");
                    }
                    li.InnerHtml = a.ToString();
                    items.AppendLine(li.ToString());
                }
                output.Content.SetContent(items.ToString());
                output.PostContent.SetContent("</ul>");
                output.Attributes.Clear();
                output.Attributes.Add("class", "pager");
            }
        }
    }
}

Taghelper.s inherit from the abstract Taghelper. class which defines a couple of virtual methods: Process and ProcessAsync. These methods are where the action is based. The vast majority of helpers implement the synchronous Process method. The Process method takes two parameters, a Taghelper.Context object and a Taghelper.Output object. The Taghelper.Context object contains information about the current tag being operated on including all of its attributes. The Taghelper.Output object represents the output generated by the TagHelper. As the Razor parser encounters an element in a view that is associated with a TagHelper, the TagHelper is invoked and generates output accordingly.

将标记与Taghelper相关联

鼓励你用这个词典命名你的taghelper课程"TagHelper" suffix e.g. 我的 Taghelper..cs 或者在这种情况下, 寻呼机Taghelper..cs. By convention, the TagHelper will target elements that have the same name as the helper up to the suffix (<my> or <pager>). If you want to ignore the suffix convention and/or target an element with a different name, you must use the TargetElement attribute to specify the name of the tag that your helper should process.

You can further refine which elements to target via the Attributes parameter of the TargetElement attribute. In the example above, three attributes are passed to the Attributes parameter: current-page, total-pages and link-url. The fact that that have been specified makes them mandatory, so this helper will only act on 寻呼机 elements that have all three attributes. Since there is a match between the target element and the TagHelper, it might seem superfluous to pass "pager" to the TargetElement attribute, but if it is omitted, an overload of the attribute is used which has the Tag property preset to a wildcard *. In other words, omitting the tag name but passing a list of required attributes will result in the TagHelper acting upon 任何 element that features all of the required attributes. If for some reason you wanted to target a limited selection of elements, you can set multiple TargetElement attributes.

生成HTML

Some local variables are declared in the Process method to hold the values obtained from the attributes, which are extracted from the Taghelper.Context.Attributes collection via their string-based index. Further processing is undertaken only if the totalPages and currentPage values can be parsed as numbers. The TagName property of the TagHelperOutput parameter is set to "div"。这将导致"pager" being replaced by "div"在最终输出中。这是必要的,否则标签将保留名称"pager"当它转换为HTML时,由于浏览器不会呈现。

The Process method's output parameter has (among others) the following properties: PreContent, Content and PostContent. PreContent appears after the opening tag specified by the TagName property and before whatever is applied to the Content property. PostContent appears after the Content, and before the closing tag specified by the TagName property.

mvc6自定义标签

Each of these properties have a SetContent method that enables content to be set. In this example, the Pre- and PostContent properties are set to an opening and closing ul tag. A StringBuilder and some TagBuilders are used to construct a set of li elements containing links that will be applied to the Content property. Finally, all of the custom attributes are removed from the TagHelper and replaced with a class attribute set to the value "pager"。如果未删除自定义属性,则它们将在最终的HTML中呈现。

Taghelper. processing for the custom tag is enabled by adding an addTagHelper directive to the _ViewImports.cshtml 在视图目录中找到的文件:

@addTagHelper "*, TagHelperTest"

"TagHelperTest" is the name of the assembly (MVC project) that the custom寻呼机TagHelper resides in, and the asterisk is a wildcard symbol representing all TagHelpers found in the assembly. If you want to enable custom TagHelpers one by one, you pass the fully qualified name of the TagHelper instead of the wildcard:

@addTagHelper "TagHelperTest.Helpers.PagerTagHelper, TagHelperTest"

与属性结合

Having shown you how to use attributes (because a fair number of existing examples feature this approach) the recommended way to pass values to the TagHelper is to bind to properties rather than querying and parsing the Taghelper.Context.Attributes collection directly. Properties can be simple ones (ints, strings etc) or complex ones. First, here's an example of the 寻呼机TagHelper modified to work with simple properties:

using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System.Text;

namespace RCTagHelperTest.Helpers
{
    [TargetElement("pager", Attributes = "total-pages, current-page, link-url")]
    public class 寻呼机TagHelper : Taghelper.
    {
        public int CurrentPage { get; set; }
        public int TotalPages { get; set; }
        [HtmlAttribute("link-url")]
        public string Url { get; set; }

        public override void Process(Taghelper.Context context, Taghelper.Output output)
        {
            output.TagName = "div";
            output.PreContent.SetContent("<ul class=\"link-list\">");

            var items = new StringBuilder();
            for (var i = 1; i <= TotalPages; i++)
            {
                var li = new TagBuilder("li");

                var a = new TagBuilder("a");
                a.MergeAttribute("href", $"{Url}?page={i}");
                a.MergeAttribute("title", $"Click to go to page {i}");
                a.InnerHtml = i.ToString();
                if (i == CurrentPage)
                {
                    a.AddCssClass("active");
                }
                li.InnerHtml = a.ToString();
                items.AppendLine(li.ToString());
            }
            output.Content.SetContent(items.ToString());
            output.PostContent.SetContent("</ul>");
            output.Attributes.Clear();
            output.Attributes.Add("class", "pager");
        }
    }
}

The main body of the Process method is almost identical to the previous example, except that is now works on the properties of the 寻呼机TagHelper class. The code for extracting the attribute values has been removed. It is no longer required as the TagHelper will take care of binding the attribute values to property names, based on a match between them, using the same rules as described earlier. Where this match doesn't occur, you can realte a specific property to an attribute by decorating the property with the HtmlAttribute attribute, passing in the name of the attribute that the property should be assigned to. You can see this above where the Url property is assigned to the incoming value applied to the link-url attribute. When you add properties to a TagHelper, Visual Studio recognises them and converts them to attributes, inserting hyphens before any uppercase characters found after the first character and then converting all characters to lower case, so the CurrentPage property becomes a current-page attribute. Then you get Intellisense support on the attributes:

mv6自定义标签 

向所有必需属性提供值,Taghelper标记名称和属性采用粗体紫色颜色表示目前已启用此Taghelper并将被处理(尽管此功能在当前RC中有点细腻):

<寻呼机 current-page="1" total-pages="6" url="~/Home/Contact/"></寻呼机>

复杂性质

如果要将大量值传递给Taghelper,如果您坚持属性或简单的属性,事情会非常笨重。如果您不小心,您可以很快结束类似于Web Forms服务器控制的内容服务器控制。好消息是标记表格也可以将复杂的物体与属性具有复杂的对象。以下示例采用标记提单,使用Google引用的内容输出公司详细信息 丰富的片段 - 添加到HTML的其他属性以提供满内容的结构。

首先,这是一个代表组织的类:

public class Organisation
{
    public string Name { get; set; }
    public string StreetAddress { get; set; }
    public string AddressLocality { get; set; }
    public string AddressRegion { get; set; }
    public string PostalCode { get; set; }
}

在控制器中创建此类的实例并将其作为模型传递给视图:

public IActionResult Contact()
{
    ViewBag.Message = "Your contact page.";

    var model = new Organisation
    {
        Name = "Microsoft Corp",
        StreetAddress = "One Microsoft Way",
        AddressLocality = "Redmond",
        AddressRegion = "WA",
        PostalCode = "98052-6399"
    };
    return View(model);
}

The CompanyTagHelper class is responsible for outputting the company details as HTML:

using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using RCTagHelperTest.Models;

namespace RCTagHelperTest.Helpers
{
    public class CompanyTagHelper : Taghelper.
    {
        public Organisation Organisation { get; set; }
        
        public override void Process(Taghelper.Context context, Taghelper.Output output)
        {
            var br = new TagBuilder("br").ToString(TagRenderMode.SelfClosing);
            output.TagName = "div";
            output.Attributes.Add("itemscope", null);
            output.Attributes.Add("itemtype", "http://schema.org/Organization");
            var name = new TagBuilder("span");
            name.MergeAttribute("itemprop", "name");
            name.SetInnerText(Organisation.Name);
            var address = new TagBuilder("address");
            address.MergeAttribute("itemprop", "address");
            address.MergeAttribute("itemscope", null);
            address.MergeAttribute("itemtype", "http://schema.org/PostalAddress");
            var span = new TagBuilder("span");
            span.MergeAttribute("itemprop", "streetAddress");
            span.SetInnerText(Organisation.StreetAddress);
            address.InnerHtml = span.ToString() + br;
            span = new TagBuilder("span");
            span.MergeAttribute("itemprop", "addressLocality");
            span.SetInnerText(Organisation.AddressLocality);
            address.InnerHtml += span.ToString() + br;
            span = new TagBuilder("span");
            span.MergeAttribute("itemprop", "addressRegion");
            span.SetInnerText(Organisation.AddressRegion);
            address.InnerHtml += span.ToString();
            span = new TagBuilder("span");
            span.MergeAttribute("itemprop", "postalCode");
            span.SetInnerText($" {Organisation.PostalCode}");
            address.InnerHtml += span.ToString();
            output.Content.SetContent(name.ToString() + address.ToString());
        }
    }
}

这就是视图中所出现的方式:

@model Organisation
@{
    ViewBag.Title = "Contact";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<company organisation="Model"></company>

This time, a property of type Organisation is added to the TagHelper, and it is automatically married up to the organisation attribute. The entire view's model is passed as a value to the attribute, and its various properties are accessed by the code within the Process method to build the HTML.

概括

Taghelper.s are a new way to dynamically generate HTML in MVC views. This article shows how to create your own TagHelpers, both through parsing attribute values from the TagHelperContext's AllAttributes collection, to by binding to properties.