MVC3 jQuery自动完成 - 用户必须选择选项,发送到服务器的选项ID

时间:2012-06-20 18:19:52

标签: jquery asp.net asp.net-mvc asp.net-mvc-3 jquery-ui-autocomplete

背景
目前在我的一个项目中,我在一些领域使用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);
}

2 个答案:

答案 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.
                }
            }
        });
    });
});

示例: http://jsfiddle.net/urEzm/

答案 1 :(得分:1)

好吧,经过大量的摆弄后,我提出了以下实施方案:

以下代码为HtmlHelper,名为@Html.AutocompleteWithHiddenFor。 HtmlHelper将根据传入的inputdata-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)