I am trying to create an AutoComplete textbox, while using an EditorTemplate
. The problem I am facing is that by using the Html.BeginCollectionItem()
extension solution (https://www.nuget.org/packages/BeginCollectionItem/), the Id's of the EditorFor()
and TextBoxFor()
methods get set dynamically and this breaks my javascript. Next to that, I do not exactly know if this is even possible (and if so, how. Below you will find how far I have come).
In the main view I have a loop to generate a partial view for each item in a collection
for (int i = 0; i < Model.VoedingCollection.Count; i++)
{
@Html.EditorFor(x => x.VoedingCollection[i], "CreateVoedingTemplate")
}
The partial view CreateVoedingTemplate.cshtml
uses the Html.BeginCollectionItem()
method
@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>
In the script above, I am trying to make a function where the user can type in the name of a standaardVoeding
item and then get results, where, after the user selects a standaardVoeding
item, the standaardVoedingId
property gets set. Then, after submitting the whole form, the controller receives the standaardVoedingId
(with all the other info as well)
So I guess Javascript somehow cannot handle the Razor View @
code and, next to that, Html.BeginCollectionItem
does something fishy because you cannot set the value of its textboxes via code during runtime. Next to that, I have tried doing alert(document.getElementById(*html.begincollectionitemId*))
and it finds the fields fine. But apparently all other methods do not work?
Is there perhaps a better solution to getting this to work?
The
BeginCollectionItem()
method alters theid
andname
attributes of the html generated by the inbuilt helpers, in your case, for the hidden input, instead ofit will generate
where
xxxx
is aGuid
.While it would be possible to use javascript to extract the
Guid
value from the textbox (assuming that was generated correctly usind@Html.TextBoxFor()
) and build the id of the associated hidden input to use as a selector, it is far easier to use class names and relative selectors.You also need to remove your scripts and css from the partial and put that in the main view (or its layout). Apart from the inline scripts, your duplicating it for each item in your collection.
Your partial needs to be
Note the class name for the textbox and its container which also includes the hidden input. Then in the main view, the script will be
Based on your comments that your are not dynamically adding or removing items in the view, then there is no point in the extra overhead or using the
BeginCollectionItem()
method. Change the name of your partial tostandaardvoeding.cshtml
(assuming that's the name of the class) and move it to the/Views/Shared/EditorTemplates
folder.Then in the main view, replace your
for
loop withwhich will generate the correct html for each item in the collection. Finally remove the
BeginCollectionItem()
method from the template so that its just