2017年5月30日 星期二

【ASP.NET MVC】Using JQuery UI AutoComplete Combobox


舊的那篇寫的太多太亂,而且 Github 上的 example 有點問題,所以重做一篇。

View
  1. @using CircleHsiao.Demo.JQueryUiCombobox.ViewModels
  2.  
  3. @model JQueryUiComboboxViewModel
  4.  
  5. @{
  6. ViewBag.Title = "JQuery UI Combobox Demo";
  7. }
  8.  
  9. <div class="row">
  10. <h2>JQuery UI Combobox Demo</h2>
  11. <div class="col-md-12">
  12. @using (Html.BeginForm()) {
  13. <p>Selected Value: @Html.DisplayFor(m => m.SelectedValue)</p>
  14. <p>@Html.DropDownListFor(m => m.SelectedValue, @Model.Options)</p>
  15. <button>submit</button>
  16. }
  17. </div>
  18. </div>

ViewModel
  1. public class JQueryUiComboboxViewModel
  2. {
  3. public IEnumerable<SelectListItem> Options { get; set; }
  4.  
  5. public string SelectedValue { get; set; }
  6. }
要用 DropDownListFor 的話要做 ViewModel,不然用 DropDownList 就好。

Controller
  1. public class HomeController : Controller
  2. {
  3. private static readonly List<SelectListItem> _options = new List<SelectListItem>()
  4. {
  5. new SelectListItem() { Text = "option1", Value = "1" },
  6. new SelectListItem() { Text = "option2", Value = "2" },
  7. new SelectListItem() { Text = "option3", Value = "3" }
  8. };
  9.  
  10. public ActionResult Index()
  11. {
  12. var vm = new JQueryUiComboboxViewModel();
  13. vm.Options = _options;
  14.  
  15. return View(vm);
  16. }
  17.  
  18. [HttpPost]
  19. public ActionResult Index(JQueryUiComboboxViewModel vm)
  20. {
  21. var selectedVal = vm.SelectedValue;
  22. vm.Options = _options;
  23. return View(vm);
  24. }
  25. }
這裡需要注意的是 Post 回來的 ViewModel 裡 Options 的狀態會流失,似乎回傳集合資料要自己做成 json 之類回來轉換,這個之後研究。

好了之後用 Nuget 載 JQuery UI。
然後去 combobox 的 demo 頁把必要的補充 js 跟 css 抓回來。
https://jqueryui.com/autocomplete/#combobox
因為很多頁都有機會用到,建議就把它做成獨立的 js 跟 css,再跟 jquery-ui 打成一包。

jquery-ui-combobox.css
  1. .custom-combobox {
  2. position: relative;
  3. display: inline-block;
  4. }
  5.  
  6. .custom-combobox-toggle {
  7. position: absolute;
  8. top: 0;
  9. bottom: 0;
  10. margin-left: -1px;
  11. padding: 0;
  12. }
  13.  
  14. .custom-combobox-input {
  15. margin: 0;
  16. padding: 5px 10px;
  17. }

jquery-ui-combobox.js
  1. (function ($) {
  2. $.widget("custom.combobox", {
  3. _create: function () {
  4. this.wrapper = $("<span>")
  5. .addClass("custom-combobox")
  6. .insertAfter(this.element);
  7.  
  8. this.element.hide();
  9. this._createAutocomplete();
  10. this._createShowAllButton();
  11. },
  12.  
  13. _createAutocomplete: function () {
  14. var selected = this.element.children(":selected"),
  15. value = selected.val() ? selected.text() : "";
  16.  
  17. this.input = $("<input>")
  18. .appendTo(this.wrapper)
  19. .val(value)
  20. .attr("title", "")
  21. .addClass("custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left")
  22. .autocomplete({
  23. delay: 0,
  24. minLength: 0,
  25. source: $.proxy(this, "_source")
  26. })
  27. .tooltip({
  28. classes: {
  29. "ui-tooltip": "ui-state-highlight"
  30. }
  31. });
  32.  
  33. this._on(this.input, {
  34. autocompleteselect: function (event, ui) {
  35. ui.item.option.selected = true;
  36. this._trigger("select", event, {
  37. item: ui.item.option
  38. });
  39. },
  40.  
  41. autocompletechange: "_removeIfInvalid"
  42. });
  43. },
  44.  
  45. _createShowAllButton: function () {
  46. var input = this.input,
  47. wasOpen = false;
  48.  
  49. $("<a>")
  50. .attr("tabIndex", -1)
  51. .attr("title", "Show All Items")
  52. .tooltip()
  53. .appendTo(this.wrapper)
  54. .button({
  55. icons: {
  56. primary: "ui-icon-triangle-1-s"
  57. },
  58. text: false
  59. })
  60. .removeClass("ui-corner-all")
  61. .addClass("custom-combobox-toggle ui-corner-right")
  62. .on("mousedown", function () {
  63. wasOpen = input.autocomplete("widget").is(":visible");
  64. })
  65. .on("click", function () {
  66. input.trigger("focus");
  67.  
  68. // Close if already visible
  69. if (wasOpen) {
  70. return;
  71. }
  72.  
  73. // Pass empty string as value to search for, displaying all results
  74. input.autocomplete("search", "");
  75. });
  76. },
  77.  
  78. _source: function (request, response) {
  79. var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
  80. response(this.element.children("option").map(function () {
  81. var text = $(this).text();
  82. if (this.value && (!request.term || matcher.test(text)))
  83. return {
  84. label: text,
  85. value: text,
  86. option: this
  87. };
  88. }));
  89. },
  90.  
  91. _removeIfInvalid: function (event, ui) {
  92.  
  93. // Selected an item, nothing to do
  94. if (ui.item) {
  95. return;
  96. }
  97.  
  98. // Search for a match (case-insensitive)
  99. var value = this.input.val(),
  100. valueLowerCase = value.toLowerCase(),
  101. valid = false;
  102. this.element.children("option").each(function () {
  103. if ($(this).text().toLowerCase() === valueLowerCase) {
  104. this.selected = valid = true;
  105. return false;
  106. }
  107. });
  108.  
  109. // Found a match, nothing to do
  110. if (valid) {
  111. return;
  112. }
  113.  
  114. // Remove invalid value
  115. this.input
  116. .val("")
  117. .attr("title", value + " didn't match any item")
  118. .tooltip("open");
  119. this.element.val("");
  120. this._delay(function () {
  121. this.input.tooltip("close").attr("title", "");
  122. }, 2500);
  123. this.input.autocomplete("instance").term = "";
  124. },
  125.  
  126. _destroy: function () {
  127. this.wrapper.remove();
  128. this.element.show();
  129. }
  130. });
  131. })(jQuery);

然後  BundleConfig.cs
  1. public static void RegisterBundles(BundleCollection bundles)
  2. {
  3. bundles.Add(new ScriptBundle("~/bundles/jquery-ui").Include(
  4. "~/Scripts/jquery-ui-1.12.1.min.js",
  5. "~/Scripts/jquery-ui-combobox.js"));
  6.  
  7. bundles.Add(new StyleBundle("~/Content/jquery-ui-css").Include(
  8. "~/Content/themes/base/jquery-ui.min.css",
  9. "~/Content/jquery-ui-combobox.css"));
  10. }

再到 _Layout.cshtml 加上 Render
  1. <html>
  2. <head>
  3. <meta charset="utf-8" />
  4. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  5. <title>@ViewBag.Title - My ASP.NET Application</title>
  6. @Styles.Render("~/Content/css")
  7. @Styles.Render("~/Content/jquery-ui-css") //
  8. @Scripts.Render("~/bundles/modernizr")
  9. </head>
  10. <body>
  11. // skip
  12.  
  13. @Scripts.Render("~/bundles/jquery")
  14. @Scripts.Render("~/bundles/bootstrap")
  15. @Scripts.Render("~/bundles/jquery-ui") //
  16. @RenderSection("scripts", required: false)
  17. </body>
  18. </html>

最後回到 View
  1. @Html.DropDownListFor(m => m.SelectedValue, @Model.Options, new { @class = "custom-combobox" })
為 DropDownList 加上 class="cutomer-combobox" 並補上
  1. @section scripts{
  2. <script>
  3. $(function () {
  4. $(".custom-combobox").combobox();
  5. });
  6. </script>
  7. }
就完成了。

補個取值
  1. $(".custom-combobox").combobox({
  2. select: function (event, ui) {
  3. console.log(ui.item.text);
  4. console.log(ui.item.value);
  5. }
  6. });

Github: Demo

沒有留言:

張貼留言