Autocomplete 在项目列表中使用自定义TagHelper(自动完成)时出现问题(与EditorFor一起显示)

Autocomplete 在项目列表中使用自定义TagHelper(自动完成)时出现问题(与EditorFor一起显示),autocomplete,asp.net-core-mvc,tag-helpers,Autocomplete,Asp.net Core Mvc,Tag Helpers,我们已经编写了一个定制的TagHelper,以通用方式处理自动完成。它有一个asp for属性,该属性定义为ModelExpression变量 autocompleteTagHelper写出一个隐藏字段(Id字段),以及一个输入字段,供autocomplete js代码使用。它最终将所选项目Id值保存到隐藏字段。此自动完成功能非常适用于表单上的多个字段 但是当使用EditorTemplate将autocompleteTagHelper合并到项目列表中以显示相同的所有项目(在模型中的列表上使用Ed

我们已经编写了一个定制的
TagHelper
,以通用方式处理自动完成。它有一个asp for属性,该属性定义为ModelExpression变量

autocomplete
TagHelper
写出一个隐藏字段(Id字段),以及一个输入字段,供autocomplete js代码使用。它最终将所选项目Id值保存到隐藏字段。此自动完成功能非常适用于表单上的多个字段

但是当使用
EditorTemplate
将autocomplete
TagHelper
合并到项目列表中以显示相同的所有项目(在模型中的列表上使用
EditorFor
)时,我们需要根据隐藏字段设置基于Z索引的名称,以便它作为项目列表返回控制器。e、 g.
z0_uu*字段*
Z1_uu*字段*

  • 我们如何获得需要加在所有字段名前面的基于Z索引的名称前缀
  • 我们必须自己弥补吗
  • 还是以某种方式从
    模型表达式中提取
  • 是否正确处理标准输入
    TagHelper
  • EditorFor
    /
    EditorTemplate
    是否是处理ASP.NET Core 1中可编辑对象列表的正确方法
自动完成标记辅助程序

public class AutocompleteTagHelper : TagHelper
{
    public ModelExpression AspFor { get; set; }

    public ModelExpression AspValue { get; set; }

    //public string AspFor { get; set; }

    public string Route { get; set; }
    public string RouteParameters { get; set; }
    public string TargetWrapper { get; set; }
    public string DisplayFormat { get; set; }
    public string ValueFormat { get; set; }

    public string ManageListCallback { get; set; }
    public string ListWrapper { get; set; }

    public string Placeholder { get; set; }

    private SkillDbContext _context;
    private readonly UserManager<ApplicationUser> _userManager;
    private IMemoryCache cache;
    public AutocompleteTagHelper(SkillDbContext Context, UserManager<ApplicationUser> userManager, IMemoryCache cache)
    {
        _context = Context;
        _userManager = userManager;
        this.cache = cache;
    }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        var hiddenVal = "";
        var displayVal = "";
        //asp-for="LandingPointId" 
        //route="/Lookups/GetLandingPoint" 
        //route-parameter="SomeOtherId" 
        //target-wrapper="form" key="Id" 
        //label="{Name} ({Code})" 
        //output="{code}"
        //AspFor.

        //get parent model from AspFor

        object thisModel = null;

        //get value properties
        if (AspValue != null)
        {
            hiddenVal = ValueFormat;
            displayVal = DisplayFormat;

            thisModel = AspValue.Model;

        }
        else if (AspFor.Model != null && !AspFor.Model.Equals((object)0))
        {
            Object Id = AspFor.Model;
            string routeMethod = Route.Split('/').Last<string>();
        }

        if(thisModel != null)
        {
            PropertyInfo[] propertyInfo = thisModel.GetType().GetProperties();
            foreach (var info in propertyInfo)
            {
                var val = info.GetValue(thisModel);
                if (val != null)
                {
                    hiddenVal = hiddenVal.Replace(("{" + info.Name + "}"), val.ToString());
                    displayVal = displayVal.Replace(("{" + info.Name + "}"), val.ToString());
                }

            }
        }
        var isAcList = ManageListCallback != null && ListWrapper != null;

        string aspForName = AspFor.Name.Replace(".", "_");
        output.TagName = "input";    // replaces <email> with <a> tag
        inputId = inputName = aspForName;
        output.Attributes["id"] = aspForName;
        output.Attributes["name"] = aspForName;
        output.Attributes["type"] = "text";

        output.Attributes["route"] = Route;
        output.Attributes["route-parameters"] = RouteParameters;
        output.Attributes["target-wrapper"] = TargetWrapper;

        output.Attributes["placeholder"] = Placeholder;

        output.Attributes["value-format"] = ValueFormat;
        output.Attributes["display-format"] = DisplayFormat;
        output.Attributes["value"] = displayVal;

        output.Attributes["class"] = "autocomplete form-control" + (isAcList?" hasList":"");

        TagBuilder HiddenValue = new TagBuilder("input");
        HiddenValue.Attributes["name"] = inputName;
        HiddenValue.Attributes["id"] = inputId + "_hidden";
        HiddenValue.Attributes["type"] = "hidden";
        HiddenValue.Attributes["value"] = hiddenVal;

        output.PreElement.SetContent(HiddenValue);

        if (isAcList)
        {
            TagBuilder AddBtn = new TagBuilder("a");

            AddBtn.Attributes["id"] = AspFor.Name.Replace(".", "_") + "_submit";
            AddBtn.Attributes["class"] = "moana-autocomplete-list-manager disabled btn btn-primary";
            AddBtn.Attributes["listwrapper"] = ListWrapper;
            AddBtn.Attributes["href"] = ManageListCallback;

            AddBtn.InnerHtml.AppendHtml("Add");
            output.PostElement.SetContent(AddBtn);
        }

    }
这是cshtml

@model List<Skill.ViewModels.AddressEditorModel>

<div class="address-info-wrapper">
    @Html.EditorFor(m => m)
<div>
@型号列表
@EditorFor(m=>m)
这是控制器方法调用

public async Task<IActionResult> UpdateAddressInfo(List<AddressEditorModel> addresses)
public异步任务UpdateAddressInfo(列表地址)
最后这是EditorTemplate

@model Skill.ViewModels.AddressEditorModel

<input type="hidden" asp-for="Id" />
<input type="hidden" asp-for="ContactTypeId" />
<input type="hidden" asp-for="AddressLink" />
<input type="hidden" asp-for="AddressLinkTo" />
<input type="hidden" asp-for="CountryId" />

<label class="col-md-12 control-label" style="padding-bottom:20px;">@Model.ContactTypeName</label>
<div class="form-group">
    <label asp-for="AddressLine" class="col-md-2 control-label">Address</label>
    <div class="col-md-10">
        <input asp-for="AddressLine" class="form-control" style="resize:both" />
        <span asp-validation-for="AddressLine" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label asp-for="Suburb" class="col-md-2 control-label">Suburb</label>
    <div class="col-md-10">
        <input asp-for="Suburb" class="form-control" />
        <span asp-validation-for="Suburb" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label asp-for="City" class="col-md-2 control-label">City</label>
    <div class="col-md-10">
        <input asp-for="City" class="form-control" />
        <span asp-validation-for="City" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label class="col-md-2 control-label">Country</label>
    <div class="col-md-10">
        <autocomplete asp-for="CountryId" route="/Lookups/GetCountry" target-wrapper="form" display-format="{Name} ({Code})" value-format="{Id}"></autocomplete>
        <span asp-validation-for="CountryId" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label asp-for="Postcode" class="col-md-2 control-label">Post Code</label>
    <div class="col-md-10">
        <input asp-for="Postcode" class="form-control" />
        <span asp-validation-for="Postcode" class="text-danger" />
    </div>
</div>
@model Skill.ViewModels.AddressEditorModel
@Model.ContactTypeName
地址
郊区
城市
国家
邮政编码
请注意,上面EditorTemplate中的autocomplete标记生成自己的内部标记(作为标记帮助器的一部分)

这是第一个地址信息页面的一部分(如firefox所示)


工作
地址
郊区
城市
国家
邮政编码
请注意,Html.EditorFor为输入名称属性生成Zn__字段名称前缀,并为输入id属性生成[n].fieldname名称

问题是如何访问索引值,或获取此前缀以从TagHelper内部附加到我们生成的输入上,即Zn_u*或[n]值,这本质上是EditorFor的索引,因为它生成重复字段


谢谢你的帮助

好的,我一直在努力工作。 我最终不得不查看Asp.Net核心源代码项目中的TagHelpers

我发现我们需要访问ViewContext.ViewData.TemplateInfo; 从TagHelper内部


或者,我们也可以使用IHtmlGenerator对象-调用GenerateTextBox、GenerateHidden等,根据需要构建标记帮助程序

您是否可以为您的模型、标记帮助程序和编辑器模板提供示例代码,以便更好地理解?
@model Skill.ViewModels.AddressEditorModel

<input type="hidden" asp-for="Id" />
<input type="hidden" asp-for="ContactTypeId" />
<input type="hidden" asp-for="AddressLink" />
<input type="hidden" asp-for="AddressLinkTo" />
<input type="hidden" asp-for="CountryId" />

<label class="col-md-12 control-label" style="padding-bottom:20px;">@Model.ContactTypeName</label>
<div class="form-group">
    <label asp-for="AddressLine" class="col-md-2 control-label">Address</label>
    <div class="col-md-10">
        <input asp-for="AddressLine" class="form-control" style="resize:both" />
        <span asp-validation-for="AddressLine" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label asp-for="Suburb" class="col-md-2 control-label">Suburb</label>
    <div class="col-md-10">
        <input asp-for="Suburb" class="form-control" />
        <span asp-validation-for="Suburb" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label asp-for="City" class="col-md-2 control-label">City</label>
    <div class="col-md-10">
        <input asp-for="City" class="form-control" />
        <span asp-validation-for="City" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label class="col-md-2 control-label">Country</label>
    <div class="col-md-10">
        <autocomplete asp-for="CountryId" route="/Lookups/GetCountry" target-wrapper="form" display-format="{Name} ({Code})" value-format="{Id}"></autocomplete>
        <span asp-validation-for="CountryId" class="text-danger" />
    </div>
</div>
<div class="form-group">
    <label asp-for="Postcode" class="col-md-2 control-label">Post Code</label>
    <div class="col-md-10">
        <input asp-for="Postcode" class="form-control" />
        <span asp-validation-for="Postcode" class="text-danger" />
    </div>
</div>
<input id="z0__Id" type="hidden" value="5" name="[0].Id" data-val-required="The Id field is required." data-val="true">
<input id="z0__ContactTypeId" type="hidden" value="1" name="[0].ContactTypeId" data-val-required="The ContactTypeId field is required." data-val="true">
<input id="z0__AddressLink" type="hidden" value="1" name="[0].AddressLink" data-val-required="The AddressLink field is required." data-val="true">
<input id="z0__AddressLinkTo" type="hidden" value="F" name="[0].AddressLinkTo">
<label class="col-md-12 control-label" style="padding-bottom:20px;">Work</label>
<div class="form-group">
<label class="col-md-2 control-label" for="z0__AddressLine">Address</label>
<div class="col-md-10">
<input id="z0__AddressLine" class="form-control" type="text" value="4a Lansdowne Street" name="[0].AddressLine" style="resize:both">
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="[0].AddressLine"> </span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="z0__Suburb">Suburb</label>
<div class="col-md-10">
<input id="z0__Suburb" class="form-control" type="text" value="Bayswater" name="[0].Suburb">
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="[0].Suburb"> </span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="z0__City">City</label>
<div class="col-md-10">
<input id="z0__City" class="form-control" type="text" value="Auckland" name="[0].City">
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="[0].City"> </span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Country</label>
<div class="col-md-10">
<input id="CountryId_hidden" type="hidden" value="1" name="CountryId">
<input id="CountryId" class="moana-autocomplete form-control ui-autocomplete-input" type="text" value="New Zealand (NZ)" display-format="{Name} ({Code})" value-format="{Id}" placeholder="" target-wrapper="form" route-parameters="" route="/Lookups/GetCountry" name="CountryId" autocomplete="off">
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="[0].CountryId"> </span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="z0__Postcode">Post Code</label>
<div class="col-md-10">
<input id="z0__Postcode" class="form-control" type="text" value="0604" name="[0].Postcode">
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="[0].Postcode"> </span>
</div>
</div>