zoukankan      html  css  js  c++  java
  • 解读ASP.NET 5 & MVC6系列(13):TagHelper

    在新版的MVC6中,微软提供了强大的TagHelper功能,以便让我们摆脱如下的臃肿代码:

    @Html.LabelFor(model => model.FullName)
    @Html.EditFor(model => model.FullName)
    @Html.ValidationMessageFor(model => model.FullName)
    

    引入新功能TagHelper以后,我们只需要这样定义就可以了,代码如下:

    @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"  /* 这里需要首先引用TagHelper所在的命名空间 */
    
    <label asp-for="FullName" class="control-label col-md-2"></label>
    <div class="col-md-10">
        <input asp-for="FullName" class="form-control" />
        <span asp-validation-for="FullName"></span>
    </div>
    

    这种方式,抛去了服务器端代码,利用自定义html属性显得更加有语义,前端人员开起来也很舒服,极大地提高了前端开发人员的效率。

    在默认的TagHelper实现里,不同的元素支持不同的自定义属性,以实现不同的用途,例如大部分元素都支持asp-for,而a元素则支持asp-controllerasp-action等,input元素最强大的,支持各种类型的type以及相关的格式。详细实现,请参考如下章节中的表格内容。

    A元素

    属性 描述
    asp-controller Controller的名称
    asp-action Action的名称
    asp-host 网站的Host
    asp-fragment URL的fragment名称
    asp-protocol 网站协议(http或https)
    asp-route Route名称
    asp-route-
    href 默认属性,如果href有值,则其它属性都不能设置任何值。

    Form元素

    属性 描述
    asp-controller Controller的名称
    asp-action Action的名称
    asp-anti-forgery
    asp-route-
    action 默认属性,如果action有值,则其它属性都不能设置任何值。

    Input元素

    属性 描述
    asp-for 模型字段的名称
    asp-format 设置Type格式,具体如下:
    格式 标准类型
    HiddenInput hidden
    Password password
    Text text
    PhoneNumber tel
    Url url
    EmailAddress email
    Date date
    DateTime datetime
    DateTime-local datetime-local
    Time time
    Byte/SByte/Int16/UInt16/Int32/UInt32/Int64/UInt64/Single/Double number
    Boolean checkbox
    Decimal text
    String text
    IFormFile file
    IEnumerable`IFormFile file

    其中关于时间的具体格式如下:

    属性 描述
    date {0:yyyy-MM-dd}
    datetime {0:yyyy-MM-ddTHH:mm:ss.fffK}
    datetime-local {0:yyyy-MM-ddTHH:mm:ss.fff}
    time {0:HH:mm:ss.fff}

    Label元素

    属性 描述
    asp-for 模型字段的名称

    textarea元素

    属性 描述
    asp-for 模型字段的名称

    span元素

    属性 描述
    asp-validation-for 模型字段的名称

    div元素

    属性 描述
    asp-validation-aummary ValidationSummary枚举值:
    ValidationSummary.All
    ValidationSummary.ModelOnly
    ValidationSummary.None。

    验证描述类型,只有选择了ValidationSummary.All和ValidationSummary.ModelOnly才能渲染该div元素。

    select元素

    属性 描述
    asp-for 模型字段名称
    asp-items 模型字段名称

    link元素

    属性 描述
    asp-href-include
    asp-href-exclude
    asp-fallback-href 默认href加载失败时的备用地址
    asp-fallback-href-include
    asp-fallback-href-exclude
    asp-fallback-test-class 判断加载失败时用到的class样式
    asp-fallback-test-property 判断加载失败时用到的class样式中的属性
    asp-fallback-test-value 判断加载失败时用到的class样式中的属性对应的值
    asp-file-version
    href 默认加载的css文件地址。

    link的使用示例如下,比如我们定义如下代码:

    <link rel="stylesheet" 
    href="//ajax.aspnetcdn.com/ajax/bootstrap-touch-carousel/0.8.0/css/bootstrap-touch-carousel.css"
    asp-fallback-href="~/lib/bootstrap-touch-carousel/css/bootstrap-touch-carousel.css"
    asp-fallback-test-class="carousel-caption" 
    asp-fallback-test-property="display" 
    asp-fallback-test-value="none" />
    

    则该段代码表示,默认先加载aspnetcdn.com上的css文件,如果加载失败了,再加载本地网站里的css文件,加载失败的判断条件是:检测carousel-caption样式十分应用上了,即该应用了该样式的元素的display属性是否等于none。运行网站后,这段代码的生成html如下:

    <link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap-touch-carousel/0.8.0/css/bootstrap-touch-carousel.css" />
    <meta name="x-stylesheet-fallback-test" class="carousel-caption" />
    <script>
        !function (a, b, c) {
            var d, e = document,
                f = e.getElementsByTagName("SCRIPT"),
                g = f[f.length - 1].previousElementSibling,
                h = e.defaultView && e.defaultView.getComputedStyle ? e.defaultView.getComputedStyle(g) : g.currentStyle;
    
            if (h && h[a] !== b) {
                for (d = 0; d < c.length; d++) {
                    e.write('<link rel="stylesheet" href="' + c[d] + '"/>')
                }
            }
        }("display", "none", ["/lib/bootstrap-touch-carousel/css/bootstrap-touch-carousel.css"]);
    </script>
    

    从中,我们看到,生成的HTML代码在link元素之后多了两个元素,一个是带有class="carousel-caption"属性的meta元素,一个是script脚本标签。其主要原理是如下:

    1. 在meta元素上应用定义的carousel-caption样式。
    2. 通过JS代码检测该meta元素的display属性是否等于none
    3. 如果不等于none,重新加载本地的备用css文件。

    注意,这里的js脚本是利用document.getElementsByTagName("SCRIPT"),获取最后一个SCRIPT标签的上一个兄弟元素的形式,来获取meta元素的。

    script元素

    属性 描述
    asp-src-include
    asp-src-exclude
    asp-fallback-src 备用js文件地址
    asp-fallback-src-include
    asp-fallback-src-exclude
    asp-fallback-test 判断默认js文件是否加载成功用到的对象
    asp-file-version
    src 默认的js文件地址。

    script标签元素的fallback功能,和link元素记载css文件类型,只不过这里判断的不是class样式,而是检测某个对象是否存在,来判断默认的js文件是否加载成功,示例如下:

    <script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.2.min.js"
        asp-fallback-src="~/lib/jquery/jquery.min.js"
        asp-fallback-test="window.jQuery">
    </script>
    

    生成后的HTML代码,相对比较简单,示例如下:

    <script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.2.min.js">
    </script>
    <script>(window.jQuery||document.write("<script src="/lib/jquery/jquery.min.js"></script>"));</script>
    

    多生成了一个script标签元素,然后判断jQuery对象是否存在,如果不存在则表示加载失败,那就再加载本地的备用js文件。

    Cache

    属性 描述
    vary-by
    vary-by-header
    vary-by-query
    vary-by-route
    vary-by-cookie
    vary-by-user
    expires-on
    expires-after
    expires-sliding
    priority
    enabled .

    利用EnvironmentTagHelper来控制不同运行环境的输出结果

    在很多情况下,我们想再开发环境使用一套配置信息,在生产环境又是另外一套,这时候就需要使用条件判断语句了,不过在新版的MVC中,使用EnvironmentTagHelper提供的Environment元素标签就可以了,示例如下:

    <environment names="Development">
        <script src="~/lib/jquery/jquery.js"></script>
        <script src="~/lib/bootstrap/js/bootstrap.js"></script>
        <script src="~/lib/hammer.js/hammer.js"></script>
        <script src="~/lib/bootstrap-touch-carousel/js/bootstrap-touch-carousel.js"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.2.min.js"
                asp-fallback-src="~/lib/jquery/jquery.min.js"
                asp-fallback-test="window.jQuery">
        </script>
        <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery">
        </script>
        <script src="//ajax.aspnetcdn.com/ajax/hammer.js/2.0.4/hammer.min.js"
                asp-fallback-src="~/lib/hammer.js/hammer.js"
                asp-fallback-test="window.Hammer">
        </script>
        <script src="//ajax.aspnetcdn.com/ajax/bootstrap-touch-carousel/0.8.0/js/bootstrap-touch-carousel.js"
                asp-fallback-src="~/lib/bootstrap-touch-carousel/js/bootstrap-touch-carousel.js"
                asp-fallback-test="window.Zepto">
        </script>
    </environment>
    

    在上述代码中,我们定于,如果是Development环境就使用本地的js文件,否则(Staging或Production环境)就先加载cdn的文件(只不过还留有备用方案)。

    该names里的值判断依据是,查找IHostingEnvironmentEnvironmentName属性,并与其进行比较,然后再进行相应的处理。

    自定义TagHelper

    MVC所有TagHelper的实现,都继承了Microsoft.AspNet.Razor.Runtime.TagHelpers.ITagHelper接口,所以我们只要实现该接口就可以实现自定义的TagHelper,该接口的定义如下:

    public interface ITagHelper
    {
        int Order { get; }
        Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
    }
    

    不过,我们一般自定义的时候,只需要继承该接口的默认实现TagHelper类,并重载其虚方法Process方法即可,如下是几个示例,我们来详细研究一下。

    1. 在a元素上直接支持controller和action属性

    public class ATagHelper : TagHelper
    {
        [Activate]
        public IUrlHelper UrlHelper { get; set; }
    
        public string Controller { get; set; }
    
        public string Action { get; set; }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            if (Controller != null && Action != null)
            {
                var methodParameters = output.Attributes.ToDictionary(attribute => attribute.Key,
                                                                        attribute => (object)attribute.Value);
    
                // 删除所有的attributes,因为路由里已经可以自动生成了
                output.Attributes.Clear();
    
                output.Attributes["href"] = UrlHelper.Action(Action, Controller, methodParameters);
    
                output.PreContent.SetContent("My ");
            }
        }
    }
    

    2. 自动识别Text文本中的链接,并提取出来

    [TargetElement("p")]
    public class AutoLinkerTagHelper : TagHelper
    {
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var childContent = await context.GetChildContentAsync();
    
            // Find Urls in the content and replace them with their anchor tag equivalent.
            output.Content.SetContent(Regex.Replace(
                childContent.GetContent(),
                @"(?:https?://|www.)(S+)",
                "<strong><a target="_blank" href="http://$0">$0</a></strong>"));
        }
    }
    

    3. 条件判断

    定义一个condiction,符合条件才显示该元素,示例如下(只有Model.Approved为true时才显示该元素):

    <p condition="Model.Approved" >&copy; @Model.CopyrightYear - My ASP.NET Application</p>
    

    实现代码如下:

    [TargetElement("div")]
    [TargetElement("style")]
    [TargetElement("p")]
    public class ConditionTagHelper : TagHelper
    {
        public bool? Condition { get; set; }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            // 如果设置了condition,并且该值为false,则不渲染该元素
            if (Condition.HasValue && !Condition.Value)
            {
                output.SuppressOutput();
            }
        }
    }
    

    4. 自定义元素的TagHelper

    如果我们要为自定义元素定义TagHelper,则我们要符合约定规范,示例代码如下:

    public class WebsiteInformationTagHelper : TagHelper
    {
        public WebsiteContext Info { get; set; }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "section";
            output.PostContent.SetContent(string.Format(
                "<p><strong>Version:</strong> {0}</p>" + Environment.NewLine +
                "<p><strong>Copyright Year:</strong> {1}</p>" + Environment.NewLine +
                "<p><strong>Approved:</strong> {2}</p>" + Environment.NewLine +
                "<p><strong>Number of tags to show:</strong> {3}</p>" + Environment.NewLine,
                Info.Version.ToString(),
                Info.CopyrightYear.ToString(),
                Info.Approved.ToString(),
                Info.TagsToShow.ToString()));
            output.SelfClosing = false;
        }
    }
    

    则使用的时候,我们需要使用website-information标签,并将info属性赋值一个强类型的值,示例如下:

    <website-information info="new WebsiteContext {
                                    Version = new Version(1, 1),
                                    CopyrightYear = 1990,
                                    Approved = true,
                                    TagsToShow = 30 }"/>
    

    其渲染结果,则是渲染成一个包含4个p元素的section元素。

    同步与推荐

    本文已同步至目录索引:解读ASP.NET 5 & MVC6系列

  • 相关阅读:
    cf B. Sereja and Suffixes
    cf E. Dima and Magic Guitar
    cf D. Dima and Trap Graph
    cf C. Dima and Salad
    最短路径问题(floyd)
    Drainage Ditches(网络流(EK算法))
    图结构练习—BFSDFS—判断可达性(BFS)
    Sorting It All Out(拓扑排序)
    Power Network(最大流(EK算法))
    Labeling Balls(拓扑)
  • 原文地址:https://www.cnblogs.com/TomXu/p/4496480.html
Copyright © 2011-2022 走看看