关于TagHelper的那些事情——自定义TagHelper(内嵌TagHelper)

内嵌TagHelper

上一篇文章中提到有时候需要设计一种内嵌的TagHelper,如下:

<my name="yy" age="35">
   <location country="China" city="Shanghai" district="PuDong"></location>
</my>

location就是一个内嵌的TagHelper,我们可以在location里设置与它相关的Attributes,有时候设置有多层内嵌的TagHelper。那么怎样设计出这样的一个内嵌的TagHelper来呢?其实它和一般的TagHelper没什么的区别,大家可以利用前面我们介绍的来设计出它的TagHelper类及Attributes。和一般的TagHelper的主要区别是,它和父TagHelper关联。如何建立这种关联?这也是这章我们需要搞清楚的。

通过前面章节学习,我们知道设计的自定义TagHelper类都会继承于抽象类TagHelper,在这个类中有二个方法:

//同步处理
public virtual void Process(TagHelperContext context, TagHelperOutput output);

//异步处理
public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output);

一般我们会根据实际情况来选择重载哪个方法。在TagHelper抽象类中,异步处理是调用同步处理方法。在这两个方法中参数相同,在这里详细介绍第一个参数context。

它的类型是TagHelperContext,主要是存放TagHelper相关的信息:

  1. AllAttributes

    TagHelper支持的所有Attribute集合,它是只读的。

  2. Items

    类型是IDictionary<object, object>,它是用来和其他TagHelper进行联系枢纽,

            /// <summary>
            /// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
            /// </summary>
            /// <remarks>
            /// This <see cref="IDictionary{object, object}"/> is copy-on-write in order to ensure items added to this 
            /// collection are visible only to other <see cref="ITagHelper"/>s targeting child elements.
            /// </remarks>
            public IDictionary<object, object> Items { get; }
    

    从上面的描述可以看出,这个集合是copy-on-write,也就是说当前TagHelper在Items获取父TagHelper中的Items信息,也可以修改,删除或者添加某一项,但是不会影响到父TagHelper的Items值,同时也会将修改后的Items信息传给其子TagHelper,子TagHelper的任何修改不会影响到它。

在上面我们提到,设计支持内嵌TagHelper类,关键是要建立父子TagHelper的联系,看到这里,我想大家都应该清楚如何建立这种关联。对了,就是利用context中Items。

主要是在TagHelper的Process方法中做以下事情。  

public virtual void Process(TagHelperContext context, TagHelperOutput output)
{
   // 从context.items获取父TagHelper信息
   // 处理自身Attributes
   // 将自身信息存放在context.items中
   // 处理自己的子TagHelper
}

更具体的例子如下:

    //定义父TagHelper
    public class PersonTagHelper : TagHelper
    {
        public string Name
        {
            get { return Person.Name; }
            set { Person.Name = value; }
        }
        public int Age
        {
            get { return Person.Age; }
            set { Person.Age = value; }
        }

        private Person _person;
        private Person Person
        {
            get
            {
                return _person ?? (_person = new Person());
            }
            set
            {
                _person = value;
            }
        }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            // 保存信息给子TagHelper使用
            context.Items["Parent"] = Person;
            // 执行并获取子TagHelper内容
            context.GetChildContentAsync();

            // 输出html元素和启动脚本
            // 这里会用到第二个参数output,后面会介绍到如何使用。
        }
    }

    // 定义子TagHelper
    public class LocationTagHelper: TagHelper
    {
        public string Country { get; set; }
        public string City { get; set; }
        public string District { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            // 获取来自父TagHelper的信息,并保存到变量里去
            var parent = context.Items["Parent"] as Person;
            // 处理Attributes的设置
            parent.Location.Country = Country;
            parent.Location.City = City;
            parent.Location.District = District;
            // 保存自身信息,便于子TagHelper使用
            context.Items["Parent"] = parent.Location;
            // 执行并获取子TagHelper内容
            context.GetChildContentAsync();
        }
    }

    // 定义了父TagHelper中对应的对象实例
   // 大家可以根据各自需求,决定是否需要定义这样一个类型
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        private Location _location;
        public Location Location
        {
            get { return _location ?? (_location = new Location()); }
        }
    }
 
    // 定义子TagHelper中数据类型
    // 大家可以根据各自需求,决定是否需要定义这样一个类型
    public class Location
    {
        public string Country { get; set; }
        public string City { get; set; }
        public string District { get; set; }
    }

对于类Person和Location的定义大家可以根据具体情况来决定是否需要。

这个例子只是简单展示如何利用Items信息来构建父子TagHelper间的联系,大家在具体的项目开发中根据实际需求,写出不一样的代码来,但有一点不变,就是要利用Items。 

posted @ 2015-07-17 00:24  奋起直追  阅读(1668)  评论(0编辑  收藏  举报