This feature would add support for directly binding Tag Helper properties to child Tag Helper instances from the source document, thus making it much simpler to author related Tag Helpers that are designed to work together in a hierarchy.
Today, we provide a number of features to assist with authoring sets of Tag Helpers that are intended to be used together in a logical hierarchy, including:
RestrictChildrenAttribute to restrict which tags are valid as children of the element a Tag Helper instance is attached toHtmlTargetElementAttribute.ParentTag to restrict which tag is valid as a parent of an element a Tag Helper instance is attached toTagHelperContext.Items to allow passing of data between different Tag Helpers attached to the same element, and between Tag Helpers attached to an element and Tag Helpers attached to that element's childrenThe use of TagHelperContext.Items to pass data between related Tag Helpers is somewhat cumbersome, e.g.:
``` c#
public class ParentChildContext
{
public string SomeData { get; set; }
}
[RestrictChildren("child")]
public class ParentTagHelper : TagHelper
{
private ParentChildContext _context;
public override void Init(TagHelperContext context)
{
_context = new ParentChildContext();
context.Items.Add(typeof(ParentChildContext), _context);
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
// Force children to process
var childContent = await output.GetChildContentAsync();
// Read context data set by child here
// _context.SomeData
}
}
[HtmlTargetElement("child", ParentTag = "parent", TagStructure = TagStructure.WithoutEndTag)]
public class ChildTagHelper : TagHelper
{
public string SomeData { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var parentContext = context.Items[typeof(ParentChildContext)];
parentContext.SomeData = SomeData;
}
}
``` html
<parent>
<child some-data="This will be accessible to the parent" />
</parent>
Tag Helper property binding would be expanded to support directly binding Tag Helper instances to properties on a Tag Helper using Tag Helpers attached to the current element's children as the source.
Binding will:
ITagHelperIEnumerable<T> where T : ITagHelper to enable simple access to multiple child Tag Helper instances of the same typeIEnumerable<T> instance to the property, irrespective of the property's initial value (i.e. it will overwrite any current value of the property)public settable properties (like other bindings)public and the type being derived from ITagHelperHtmlAttributeNotBoundAttribute on the property that would otherwise be boundHere is the example from above reimplemented with the proposed behavior implied:
``` c#
[RestrictChildren("child")]
public class ParentTagHelper : TagHelper
{
public ChildTagHelper Child { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
// Force children to process
var childContent = await output.GetChildContentAsync();
// Read data directly from child here
// Child.SomeData
}
}
[HtmlTargetElement("child", ParentTag = "parent", TagStructure = TagStructure.WithoutEndTag)]
public class ChildTagHelper : TagHelper
{
public string SomeData { get; set; }
}
``` html
<parent>
<child some-data="This will be accessible to the parent" />
</parent>
``` c#
///
[HtmlTargetElement("*")]
public class DomTagHelper : TagHelper
{
public DomTagHelper Parent { get; set; }
public IEnumerable<DomTagHelper> Children { get; set; }
public override void Init(TagHelperContext context)
{
// Set parent on all children
foreach (var child in children)
{
child.Parent = this;
}
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
// Run children
var childContent = await output.GetChildContentAsync();
//
}
}
```
This could be extended into a more general TagHelperAwareness concept with common constructs. Awareness determines helper participation scope and drives things like automatic binding. I've been experimenting in this area and use four basic types of awareness.
Self Awareness -> Tag. This is the current case. A TagHelper is aware only of itself.
Positional Awareness -> Parent/Parents, Child/Children. These make the TH aware of its parent and/or children tag or tags depending on settings. This is much like the examples.
Scope Awareness -> Page/Type. Normally I use this to make the TH aware of the Page. It automatically binds/sets things like HtmlHelper and Contextualize. Helps when TH shares info through TempData. But the category is Type-based, and it can reach out farther. For example, a routing tag inspects a NamedRoute, finds out things like the operation parameters on the controller, and then builds an appropriate request.
Referential Awareness -> Group/Master. This extends awareness into the layout level using attributes. This could go several ways, but example use would be "cloning" for more complex helpers. You can set values on one instance that has an id and point others to that one. They don't need a positional relationship.
<Complex id="C1" para-set-one="(3, 6, 12, 8)" injected-para="@theme.Complex1" data="@somedata"/>
<other stuff>...</other stuff>
<div>
<Complex clone="C1" data="@otherdata" />
</div>
This would set properties on the clone to those from the master. The properties could require [Cloneable] attribute.
Displaying all items in a logical group, regardless of tag type or location. These helpers implement ITagGroup.Visible features. If a tag doesn't contain data it becomes invisible. The other tags inspect the group, and check the members for visibility. If any one is not visible each turns itself off.
<tagone group="g1" para="sdfsd/>
...
<tagtwo group="g1" p1="werwe" p2="ityut"/>
...
<tagthree group="g1" p1="werwe" p2="ityut"/>
Group collections are built at runtime at Page level. Rules can be run at the page level with the page calling into the helper, or the helper can access a group and other tags and then manipulate itself and/or them. A simple approach is having something on the interface that leads to a rule in the helper code to do
Groups("g1").Any(!Visible)) Visible = false;
Thanks for contacting us. We're closing this issue as there was no community involvement here for quite a while now.
Most helpful comment
This could be extended into a more general TagHelperAwareness concept with common constructs. Awareness determines helper participation scope and drives things like automatic binding. I've been experimenting in this area and use four basic types of awareness.
Self Awareness -> Tag. This is the current case. A TagHelper is aware only of itself.
Positional Awareness -> Parent/Parents, Child/Children. These make the TH aware of its parent and/or children tag or tags depending on settings. This is much like the examples.
Scope Awareness -> Page/Type. Normally I use this to make the TH aware of the Page. It automatically binds/sets things like HtmlHelper and Contextualize. Helps when TH shares info through TempData. But the category is Type-based, and it can reach out farther. For example, a routing tag inspects a NamedRoute, finds out things like the operation parameters on the controller, and then builds an appropriate request.
Referential Awareness -> Group/Master. This extends awareness into the layout level using attributes. This could go several ways, but example use would be "cloning" for more complex helpers. You can set values on one instance that has an id and point others to that one. They don't need a positional relationship.
This would set properties on the clone to those from the master. The properties could require
[Cloneable]attribute.Displaying all items in a logical group, regardless of tag type or location. These helpers implement ITagGroup.Visible features. If a tag doesn't contain data it becomes invisible. The other tags inspect the group, and check the members for visibility. If any one is not visible each turns itself off.
Group collections are built at runtime at Page level. Rules can be run at the page level with the page calling into the helper, or the helper can access a group and other tags and then manipulate itself and/or them. A simple approach is having something on the interface that leads to a rule in the helper code to do