Basically, I'm extending on a previously answered question (Updating related entities) so that it is a Custom Tag Helper.
I want to send the custom tag helper a list of phones related to the user and generate a textbox for each.
So, lets assume I have the following syntax:
<user-phones phones="@Model.UserPhones" />
Here is the start I have for the Custom Tag Helper:
public class UserPhonesTagHelper : TagHelper
{
private readonly IHtmlGenerator _htmlGenerator;
private const string ForAttributeName = "asp-for";
public List<UserPhones> Phones { get; set; }
[ViewContext]
public ViewContext ViewContext { set; get; }
[HtmlAttributeName(ForAttributeName)]
public ModelExpression For { get; set; }
public UserPhonesTagHelper(IHtmlGenerator htmlGenerator)
{
_htmlGenerator = htmlGenerator;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
output.TagMode = TagMode.StartTagAndEndTag;
//output.Attributes.Add("class", "form-group");
StringBuilder sbRtn = new StringBuilder();
for (int i = 0; i < Phones.Count(); i++)
{
//NEED HELP HERE
}
output.Content.SetHtmlContent(sbRtn.ToString());
}
}
Within the for
loop, how could I generate a textbox and hidden inputs related to the current `UserPhone' entity in the iteration? I would need this to remain bound when the parent razor page is posted as well.
My thought is a method like so would help. BUT, I do not know how to pass the ModelExpression
from the for
loop to the method
private void WriteInput(TextWriter writer)
{
var tagBuilder = _htmlGenerator.GenerateTextBox(
ViewContext,
For.ModelExplorer,
For.Name,
value: null,
format: null,
htmlAttributes: new { @class = "form-control" });
tagBuilder.WriteTo(writer, htmlEncoder);
}
Thank you again for all your help... still learning asp.net core.
You're almost there.
Design
The difficulty here is that we need to construct an expression for unknown properties. Let's say when you want to use the
<user-phones asp-for=""/>
in a much more higher level, considering the following code :Inside the tag helper, we might assume the default name of each property to be
UserPhones[<index>].<property-name>
. But that's not always that case, users might want to change it toM0.M2....Mx.UserPhones[<index>].<property-name>
. However, it's not possible to know how many levels there will be at compile-time.So we need an attribute of
ExpressionFilter
to convert the default expression to target expression :The
ExpressionFilter
here is a simple delegate to convert expression string.Show me the Code
I simply copy most of your code and make a little change :
The
ProcessAsync()
above only shows a label and input forUserPhoneId
andPhoneNumber
field. If you would like to show all the properties automatically, you can simply change the method to be :the default expression string for some field is generated by:
eg :
AppUser.UserPhones[i].<property-name>
Surely it won't apply for all cases, we can custom our own
expression-filter
to convert the expression as we like :Test case
The first part is generated by partial view and the second part is generated by
user-phones
: