razor - 复杂的自定义标签助手

标签 razor asp.net-core tag-helpers

基本上,我扩展了之前回答的问题 ( Updating related entities ),使其成为一个自定义标记帮助程序。

我想向自定义标签助手发送与用户相关的电话列表,并为每个电话生成一个文本框。

所以,假设我有以下语法:

<user-phones phones="@Model.UserPhones" />

这是我对自定义标记帮助程序的开始:

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());
    }
}

for 循环中,如何在迭代中生成与当前“UserPhone”实体相关的文本框和隐藏输入?当父 Razor 页面发布时,我需要它保持绑定(bind)。

我的想法是这样的方法会有所帮助。但是,我不知道如何将 ModelExpressionfor 循环传递到方法

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);
    }

再次感谢您的帮助...仍在学习 asp.net core。

最佳答案

你就快到了。

设计

这里的困难在于我们需要为未知属性构造一个表达式。假设您何时想使用 <user-phones asp-for=""/>在更高的层次上,考虑以下代码:

@model M0

@{
    var M1 = GetM1ByMagic(M0);
}
<user-phones asp-for="@M1.M2....Mx.UserPhones">
</user-phones>

在标签助手中,我们可以假设每个属性的默认名称为 UserPhones[<index>].<property-name> 。但情况并非总是如此,用户可能希望将其更改为 M0.M2....Mx.UserPhones[<index>].<property-name> 。但是,不可能知道编译时会有多少层。

所以我们需要一个属性ExpressionFilter将默认表达式转换为目标表达式:

public class UserPhonesTagHelper : TagHelper
{

    [HtmlAttributeName("expression-filter")]
    public Func<string, string> ExpressionFilter { get; set; } = e => e;

    // ...
}

ExpressionFilter这是一个用于转换表达式字符串的简单委托(delegate)。

显示代码

我只是复制您的大部分代码并进行一些更改:

public class UserPhonesTagHelper : TagHelper
{
    private readonly IHtmlGenerator _htmlGenerator;
    private const string ForAttributeName = "asp-for";


    public IList<UserPhones> Phones { get; set; }

    [ViewContext]
    public ViewContext ViewContext { set; get; }

    [HtmlAttributeName(ForAttributeName)]
    public ModelExpression For { get; set; }

    public UserPhonesTagHelper(IHtmlGenerator htmlGenerator)
    {
        _htmlGenerator = htmlGenerator;
    }

    [HtmlAttributeName("expression-filter")]
    public Func<string, string> ExpressionFilter { get; set; } = e => e;

    // a helper method that generate a label and input for some property
    private TagBuilder GenerateSimpleInputForField( int index ,PropertyInfo pi)
    {
        var instance = Phones[index];// current instance of a single UserPhone
        var name = pi.Name;          // property name : e.g. "PhoneNumberId"
        var v = pi.GetValue(instance);

        var div = new TagBuilder("div");
        div.AddCssClass("form-group");

        var expression = this.ExpressionFilter(For.Name + $"[{index}].{name}");
        var explorer = For.ModelExplorer.GetExplorerForExpression(typeof(IList<UserPhones>), o =>v);

        var label = _htmlGenerator.GenerateLabel( ViewContext, explorer, expression, name, new { } );
        div.InnerHtml.AppendHtml(label);

        var input = _htmlGenerator.GenerateTextBox( ViewContext, explorer, expression, v, null, new { @class = "form-control" } );
        div.InnerHtml.AppendHtml(input);
        return div;
    }

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "div";
        output.TagMode = TagMode.StartTagAndEndTag;

        var type = typeof(UserPhones);
        PropertyInfo phoneId= type.GetProperty("UserPhoneId");
        PropertyInfo phoneNumber= type.GetProperty("PhoneNumber");

        for (int i = 0; i< Phones.Count();i++) {
            var div1 = this.GenerateSimpleInputForField(i,phoneId);
            var div2 = this.GenerateSimpleInputForField(i,phoneNumber);

            output.Content.AppendHtml(div1);
            output.Content.AppendHtml(div2);
        }
    }
}
  1. ProcessAsync()上面仅显示 UserPhoneId 的标签和输入和PhoneNumber field 。如果您想自动显示所有属性,您只需将方法更改为:

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "div";
        output.TagMode = TagMode.StartTagAndEndTag;
    
        for (int i = 0; i < Phones.Count(); i++)
        {
            var pis = typeof(UserPhones).GetProperties();
            foreach (var pi in pis)
            {
                var div = this.GenerateSimpleInputForField(i, pi);
                output.Content.AppendHtml(div);
            }
        }
    }
    
  2. 某些字段的默认表达式字符串由以下方式生成:

    get_the_name_by('asp-for') +'[<index>]'+'<property-name>'  
    

    例如:AppUser.UserPhones[i].<property-name>

    当然这并不适用于所有情况,我们可以定制自己的 expression-filter根据需要转换表达式:

    // use <user-phones> in view file :
    
    // custom our own expression filter :
    @{
        var regex= new System.Text.RegularExpressions.Regex(@"...");
    
        Func<string, string> expressionFilter = e => {
            var m = regex.Match(e);
            // ...
            return m.Groups["expression"].Value;
        };
    }
    <user-phones phones="@Model.AppUser.UserPhones" 
        asp-for="@Model.AppUser.UserPhones" 
        expression-filter="expressionFilter">
    </user-phones>
    

测试用例

<div class="row">
    @await Html.PartialAsync("_NameAndID", Model.AppUser)
</div>

<form method="post">
    <div class="row">
        <user-phones phones="@Model.AppUser.UserPhones" asp-for="@Model.AppUser.UserPhones" expression-filter="e => e.Substring(8)"></user-phones>
    </div>

    <button type="submit">submit</button>
</form>

第一部分由部分 View 生成,第二部分由 user-phones 生成:

enter image description here

关于razor - 复杂的自定义标签助手,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53395905/

相关文章:

c# - 多行变成单行多列

c# - razor 中可以使用哪些 c# 类和函数?

c# - 将 GraphQL 从 .NET core 2.2 升级到 3.0

c# - .NET Core IServiceScopeFactory.CreateScope() 与 IServiceProvider.CreateScope() 扩展

c# - 预 ASP.NET Core MVC 应用程序中的 Razor 标记帮助程序

asp.net-core - 标签助手智能感知的 xml 注释

.net - 替换字符串中的空格,mvc razor

c# - 防止后退按钮

c# - 使用自定义 SignInManager

css - TagHelpers 根据验证属性为 LabelTagHelper 添加自定义类 [必需]