当使用Html.BeginCollectionItem时,MVC自动完成编辑器

MVC AutoComplete EditorFor while using Html.BeginCollectionItem

本文关键字:编辑器 MVC Html BeginCollectionItem      更新时间:2023-09-26

我试图创建一个自动完成文本框,而使用EditorTemplate。我面临的问题是,通过使用Html.BeginCollectionItem()扩展解决方案(https://www.nuget.org/packages/BeginCollectionItem/), EditorFor()TextBoxFor()方法的Id得到动态设置,这打破了我的javascript。除此之外,我不知道这是否可能(如果可能的话,又是如何做到的)。下面你会发现我已经走了多远)。

在主视图中,我有一个循环,为集合中的每个项目生成部分视图

for (int i = 0; i < Model.VoedingCollection.Count; i++)
{
    @Html.EditorFor(x => x.VoedingCollection[i], "CreateVoedingTemplate")
}

部分视图CreateVoedingTemplate.cshtml使用Html.BeginCollectionItem()方法

@using (Html.BeginCollectionItem("VoedingCollection"))
{
    string uniqueId = ViewData.TemplateInfo.HtmlFieldPrefix.Replace('[', '_').Replace(']', '_').ToString();
    string searchId = "Search_";
    string standaardVoedingId = "StandaardVoeding_";
    foreach (KeyValuePair<string, object> item in ViewData)
    {
        if (item.Key == "Count")
        {
            searchId = searchId + item.Value.ToString();
            standaardVoedingId = standaardVoedingId + item.Value.ToString();
        }
    }
    <div class="form-horizontal">   
       <div class="form-group" id=@standaardVoedingId>
            @Html.LabelFor(model => model.fk_standaardVoedingId, "Voeding naam", htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.HiddenFor(model => model.fk_standaardVoedingId)
                    <input type="text" id='@searchId' placeholder="Search for a product"/>
                </div>
       </div>
    </div>
    <script type="text/javascript">
        var id = '@uniqueId' + '_fk_standaardVoedingId'.toString();
        var search = '@searchId'.toString();
        var url = '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "AgendaApi" })';
        $(document.getElementById(search)).autocomplete({
            source: function (request, response) {
                $.ajax({
                    url: url,
                    data: { query: request.term },
                    dataType: 'json',
                    type: 'GET',
                    success: function (data) {
                        response($.map(data, function (item) {
                            return {
                                label: item.standaardVoedingNaam,
                                value: item.standaardVoedingId
                            }
                        }));
                    }
                })
            },
            select: function (event, ui) {
                $(document.getElementById(search)).val(ui.item.label);
                //$('#id').val(ui.item.value);
                document.getElementById(id).value = ui.item.value;
                return false;
            },
            minLength: 1
        });
    </script>
}
<link href="~/Content/SearchBox/jquery-ui.css" rel="stylesheet" />
<script src="~/Scripts/SearchBox/jquery-1.9.1.js"></script>
<script src="~/Scripts/SearchBox/jquery-ui.js"></script>

在上面的脚本中,我试图创建一个函数,用户可以在其中键入standaardVoeding项目的名称,然后获得结果,其中,在用户选择standaardVoeding项目后,设置standaardVoedingId属性。然后,在提交整个表单之后,控制器接收standaardVoedingId(以及所有其他信息)

所以我猜Javascript在某种程度上不能处理剃刀视图@代码,其次,Html.BeginCollectionItem做了一些可疑的事情,因为你不能在运行时通过代码设置其文本框的值。接下来,我试过做alert(document.getElementById(*html.begincollectionitemId*)),它发现字段很好。但显然其他方法都不起作用?

是否有更好的解决方案来让这个工作?

BeginCollectionItem()方法改变了由内置助手生成的html的idname属性,在您的示例中,用于隐藏输入,而不是

<input ... name="fk_standaardVoedingId" .... />

会生成

<input ... name="VoedingCollection[xxxx].fk_standaardVoedingId" .... />

其中xxxxGuid

虽然可以使用javascript从文本框中提取Guid值(假设使用@Html.TextBoxFor()正确生成)并构建相关隐藏输入的id以用作选择器,但使用类名和相对选择器要容易得多。

您还需要从部分中删除脚本和css,并将其放在主视图(或其布局)中。除了内联脚本之外,您还需要为集合中的每个项目复制它。

你的部分需要是

@using (Html.BeginCollectionItem("VoedingCollection"))
{
    <div class="form-horizontal">   
       <div class="form-group">
            @Html.LabelFor(model => model.fk_standaardVoedingId, "Voeding naam", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10 item"> // add class name
                @Html.HiddenFor(model => model.fk_standaardVoedingId)
                <input type="text" class="search" placeholder="Search for a product"/>
            </div>
       </div>
    </div>
}

请注意文本框及其容器的类名,其中还包括隐藏输入。然后在主视图中,脚本将是

<script type="text/javascript">
    var url = '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "AgendaApi" })';
    // Attach the script to all textboxes
    $('.search').autocomplete({
        source: function (request, response) {
            $.ajax({
                url: url,
                data: { query: request.term },
                dataType: 'json',
                type: 'GET',
                success: function (data) {
                    response($.map(data, function (item) {
                        return {
                            label: item.standaardVoedingNaam,
                            value: item.standaardVoedingNaam, // this needs to be the name
                            id: item.standaardVoedingId // add property for the id
                        }
                    }));
                }
            })
        },
        select: function (event, ui) {
            // Get the associated hidden input
            var input = $(this).closest('.item').find('input[type="hidden"]');
            // Set the value of the id property
            input.val(ui.item.id);
        },
        minLength: 1
    });
</script>

根据你的评论,你没有动态地添加或删除视图中的项目,那么在额外的开销或使用BeginCollectionItem()方法是没有意义的。将部分的名称更改为standaardvoeding.cshtml(假设这是类的名称)并将其移动到/Views/Shared/EditorTemplates文件夹。

然后在主视图中,将for循环替换为

@Html.EditorFor(m => m.VoedingCollection)

,它将为集合中的每个项生成正确的HTML。最后,从模板中删除BeginCollectionItem()方法,使其仅为

<div class="form-horizontal">   
   <div class="form-group">
        @Html.LabelFor(m => m.fk_standaardVoedingId, "Voeding naam", htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10 item"> // add class name
            @Html.HiddenFor(m => m.fk_standaardVoedingId)
            <input type="text" class="search" placeholder="Search for a product"/>
        </div>
   </div>
</div>