背景
目前在我的一个项目中,我在一些领域使用jQuery autocomplete
。
要提供上下文,应用程序会记录Runs
。每个Run
必须与Route
相关联。一个Route
含义,用户在哪里跑。
当用户输入Route
时,Routes
选项会显示autocomplete
的列表,但数据库需要RouteID
进行验证。
为了弥补这一点,我将RouteID
存储在HiddenFor
HtmlHelper中。当用户从autocomplete
中选择路线时,会HiddenFor
被分配。
我的问题是什么
如果用户输入Route
的全名,而不是从autocomplete
列表中选择它或输入不存在的Route
,则HiddenFor
将无法获取分配。发生这种情况时,我必须通过其名称找到Route
并验证它是否存在于服务器上。
我希望不必为每个autocomplete
创建这种解决方法。
底线
反正有没有让autocomplete
行为更像select list
?我希望用户别无选择,只能从autocomplete
列表中选择一个选项的文本,并将所选选项的值发送到服务器。
如果我必须坚持使用HiddenFor
方法,是否至少有一种方法可以强制用户从autocomplete
列表中选择一个选项?
以下是我目前使用的代码
标记式
@Html.LabelFor(model => model.RouteID, "Route")
<input type="text" data-autocomplete-url="@Url.Action("../Route/GetRoutesByUser")" />
@Html.HiddenFor(m => m.RouteID)
的jQuery
$('*[data-autocomplete-url]')
.each(function () {
$(this).autocomplete({
source: $(this).data("autocomplete-url"),
minLength: 2,
select: function (event, ui) {
log(ui.item.id, ui.item.name);
}
});
});
代码
public ActionResult GetRoutesByUser(string term)
{
var routeList = db.Routes.Where(r => r.Name.Contains(term))
.Take(5)
.Select(r => new { id = r.RouteID, label = r.Name, name = "RouteID"});
return Json(routeList, JsonRequestBehavior.AllowGet);
}
答案 0 :(得分:1)
我会使用change
事件,如果未选择项目,则清除input
的值:
$('*[data-autocomplete-url]')
.each(function () {
$(this).autocomplete({
source: $(this).data("autocomplete-url"),
minLength: 2,
select: function (event, ui) {
log(ui.item.id, ui.item.name);
},
change: function (event, ui) {
if (!ui.item) {
this.value = '';
} else {
// Populate your hidden input.
}
}
});
});
});
答案 1 :(得分:1)
好吧,经过大量的摆弄后,我提出了以下实施方案:
以下代码为HtmlHelper
,名为@Html.AutocompleteWithHiddenFor
。 HtmlHelper将根据传入的input
和data-autocomplete-url
创建一个controller
HTML元素,其action
属性。
如果input
元素需要value
,那么您也可以传递它。将为传入的HiddenFor
媒体资源创建Model
,同时也会为ValidationMessageFor
创建Model
。
现在我所要做的就是使用@Html.AutocompleteWithHiddenFor
,并使用控制器和动作(以及可能的值)传递我需要的任何表达式以获取自动完成功能并将ID传递给服务器而不是文本。
<强>的jQuery 强>
$(function () {
function log(id, name) {
var hidden = $('#' + name);
hidden.attr("value", id);
}
$('*[data-autocomplete-url]')
.each(function () {
$(this).autocomplete({
source: $(this).data("autocomplete-url"),
minLength: 2,
select: function (event, ui) {
log(ui.item.id, ui.item.name);
},
change: function (event, ui) {
if (!ui.item) {
this.value = '';
} else {
log(ui.item.id, ui.item.name);
}
}
});
});
});
AutocompleteHelper类
public static class AutocompleteHelper
{
/// <summary>
/// Given a Model's property, a controller, and a method that belongs to that controller,
/// this function will create an input html element with a data-autocomplete-url property
/// with the method the autocomplete will need to call the method. A HiddenFor will be
/// created for the Model's property passed in, so the HiddenFor will be validated
/// and the html input will not.
/// </summary>
/// <returns></returns>
public static MvcHtmlString AutocompleteWithHiddenFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, string controllerName, string actionName, object value = null)
{
// Create the URL of the Autocomplete function
string autocompleteUrl = UrlHelper.GenerateUrl(null, actionName,
controllerName,
null,
html.RouteCollection,
html.ViewContext.RequestContext,
includeImplicitMvcValues: true);
// Create the input[type='text'] html element, that does
// not need to be aware of the model
String textbox = "<input type='text' data-autocomplete-url='" + autocompleteUrl + "'";
// However, it might need to be have a value already populated
if (value != null)
{
textbox += "value='" + value.ToString() + "'";
}
// close out the tag
textbox += " />";
// A validation message that will fire depending on any
// attributes placed on the property
MvcHtmlString valid = html.ValidationMessageFor(expression);
// The HiddenFor that will bind to the ID needed rather than
// the text received from the Autocomplete
MvcHtmlString hidden = html.HiddenFor(expression);
string both = textbox + " " + hidden + " " + valid;
return MvcHtmlString.Create(both);
}
}
查看强>
@Html.LabelFor(model => model.RouteID, "Route")
@Html.AutocompleteWithHiddenFor(model => model.RouteID, "Route", "GetRoutesByUser")
或者如果它需要值
@Html.LabelFor(model => model.Route, "Route")
@Html.AutocompleteWithHiddenFor(model => model.RouteID, "Route", "GetRoutesByUser", @Model.RouteName)