問題
當你查找一些特殊的東西,當你輸入準確的詞時,找到它可能是困難的(或者很耗時)。在輸入的時候展示出結果(自動完成),使查找變得更簡單。
解決方案
使用JQuery自動完成插件,更新現有圖書列表頁面上的搜索,當用戶鍵入的時候立即顯示結果。
討論
自動完成插件是不會象jQuery基本庫一樣自動包含在MVC項目中的,所以需要做的第一件事就是的是下載插件
訪問http://jquery.com/。兩個主要的文件是必需的:JavaScript文件和CSS文件。把新下載的javascript文件放到你MVC應用程序的script 文件夾下。CSS文件可以直接添加到您的content目錄。
這個配方也將介紹在view中使用 rendering sections。在shared文件夾下layout中自動添加了2個javascript文件和1個css文件。這些是Ajax和不唐突的Ajax和網站主css文件。每次加載的內容越多,頁面視圖加載越慢。與其在每個頁面都去包含可能不必要的javascript和css 文件,不如在layout中添加一個新的RenderSection()。這允許特別的view在<head>標簽去加載特別的javascript或css,但不是每頁都添加他們。
下邊是一個更新后的Views/Shared/_Layout.cshtml,他使用了一個新的RenderSection()。
<!DOCTYPE html><html><head><title>_Mobile</title><link href="@Url.Content(" rel="external nofollow" rel="external nofollow" ~/Content/Site.css")" rel="stylesheet" type="text/css" /><script src="@Url.Content("~/Scripts/jquery-1.6.2.min.js")" type="text/javascript"></script><script type="text/javascript">$(document).ready(function () {if (window.innerWidth <= 480) {$("link[rel=stylesheet]").attr({ href: "@Url.Content("~/Content/jquery.mobile-1.0b1.min.css")" });}});</script>@RenderSection("JavaScriptAndCSS", required: false)</head><body><div class="page" data-role="page"><div id="header" data-role="header"><div id="title"><h1>My MVC Application</h1></div><div id="logindisplay" class="ui-bar">@Html.Partial("_LogOnPartial")[ @Html.ActionLink("English", "ChangeLanguage", "Home", new { language = "en" }, null) ][ @Html.ActionLink("Français", "ChangeLanguage", "Home", new { language = "fr" }, null) ]</div><div id="menucontainer" class="ui-bar"><ul id="menu"><li>@Html.ActionLink("Home", "Index", "Home", null, new Dictionary<string, object> {{ "data-role", "button" }})</li><li>@Html.ActionLink("About", "About", "Home", null, new Dictionary<string, object> { { "data-role", "button" }})</li></ul></div></div><div id="main" data-role="content">@RenderBody()</div><div id="footer" data-role="footer"></div></div></body></html>
主要的CSS文件和核心的JQuery文件被留下來了,因為css在每個也都需要,并且絕大多數網頁也需要JQuery。然而新的JQuery文件和不唐突的AJAX不是每個頁面都需要的。
現在,有兩種方式使用Autocomplete 插件:
1.在javascript中設置要搜索的數據。
2.當用戶輸入時通過ajax檢索。
在我使用這個插件的經驗看來,我發現使用解決方案1時自動完成更快。因為它并不需要每次從數據庫中請求數據。然而,使用這種解決方案的限制:只有這么多字符,可傳遞到function中,大量的JavaScript可能會導致用戶的計算機上頁面加載緩慢。經過一些試驗和錯誤,我已經確定神奇的數字是大約40,000個結果。如果結果數量超過此,最好使用選項2;否則,始終堅持,因為搜索選項1是瞬時,而不是有輕微的延遲。
在這個例子中,將搜索書籍,我們沒有超過40000,所以將使用選項1。BooksController現在必須更新,以設置ViewBag為book title。自動完成功能需要支持一個JavaScript數組的支持,所以書將管道(|)分開。然后在view中,書將被轉換到一個數組,使用JavaScript的split()函數。當用戶完成鍵入他們的結果,他們應該有選擇完全匹配標題,因此這個函數將被更新。如果只有1本書返回并且用戶執行了搜索,它會自動重定向到本書詳細介紹頁面。
我們要在bookcontroller 中更新Index Action 并添加一個私有方法名為:FormatBooksForAutocomplete。
代碼如下:
using System;using System.Collections.Generic;using System.Data;using System.Data.Entity;using System.Linq;using System.Linq.Dynamic;using System.Web;using System.Web.Mvc;using MvcApplication.Models;using MvcApplication.Utils;using PagedList;namespace MvcApplication.Controllers{ public class BooksController : Controller{private BookDBContext db = new BookDBContext();//// GET: /Books/[OutputCache(Duration = Int32.MaxValue, SqlDependency = "MvcApplication.Models.BookDBContext:books", VaryByParam = "sortOrder;filter;page")]public ActionResult Index(string sortOrder, string filter, string Keyword, int page = 1){#region ViewBag ResourcesViewBag.Title = Resources.Resource1.BookIndexTitle;ViewBag.CreateLink = Resources.Resource1.CreateLink;ViewBag.TitleDisplay = Resources.Resource1.TitleDisplay;ViewBag.IsbnDisplay = Resources.Resource1.IsbnDisplay;ViewBag.SummaryDisplay = Resources.Resource1.SummaryDisplay;ViewBag.AuthorDisplay = Resources.Resource1.AuthorDisplay;ViewBag.ThumbnailDisplay = Resources.Resource1.ThumbnailDisplay;ViewBag.PriceDisplay = Resources.Resource1.PriceDisplay;ViewBag.PublishedDisplay = Resources.Resource1.PublishedDisplay;ViewBag.EditLink = Resources.Resource1.EditLink;ViewBag.DetailsLink = Resources.Resource1.DetailsLink;ViewBag.DeleteLink = Resources.Resource1.DeleteLink;#endregion#region ViewBag Sort ParamsViewBag.TitleSortParam = (sortOrder == "Title") ? "Title desc" : "Title";ViewBag.IsbnSortParam = (sortOrder == "Isbn") ? "Isbn desc" : "Isbn";ViewBag.AuthorSortParam = (sortOrder == "Author") ? "Author desc" : "Author";ViewBag.PriceSortParam = (sortOrder == "Price") ? "Price desc" : "Price";ViewBag.PublishedSortParam = (String.IsNullOrEmpty(sortOrder)) ? "Published desc" : "";// Default the sort orderif (String.IsNullOrEmpty(sortOrder)){sortOrder = "Published desc";}ViewBag.CurrentSortOrder = sortOrder;#endregionvar books = from b in db.Books select b;#region Keyword Searchif (!String.IsNullOrEmpty(Keyword)){books = books.Where(b => b.Title.ToUpper().Contains(Keyword.ToUpper()) || b.Author.ToUpper().Contains(Keyword.ToUpper()));// Should we redirect because of only one result?if (books.Count() == 1){Book book = books.First();return RedirectToAction("Details", new { id = book.ID });}}ViewBag.CurrentKeyword = String.IsNullOrEmpty(Keyword) ? "" : Keyword;#endregion#region Filter switchswitch (filter){case "NewReleases":var startDate = DateTime.Today.AddDays(-14);books = books.Where(b => b.Published <= DateTime.Today.Date && b.Published >= startDate);break;case "ComingSoon":books = books.Where(b => b.Published > DateTime.Today.Date);break;default:// No filter neededbreak;}ViewBag.CurrentFilter = String.IsNullOrEmpty(filter) ? "" : filter;#endregionbooks = books.OrderBy(sortOrder);int maxRecords = 1;int currentPage = page - 1;// Get all book titlesViewBag.BookTitles = FormatBooksForAutocomplete();return View(books.ToPagedList(currentPage, maxRecords));}private string FormatBooksForAutocomplete(){string bookTitles = String.Empty;var books = from b in db.Books select b;foreach (Book book in books){if (bookTitles.Length > 0){bookTitles += "|";}bookTitles += book.Title;}return bookTitles;}//// GET: /Books/Details/5public ActionResult Details(int id = 0, string bookTitle = ""){Book book = db.Books.Find(id);return View(book);}//// GET: /Books/Createpublic ActionResult Create(){return View();} //// POST: /Books/Create[HttpPost]public ActionResult Create(Book book, HttpPostedFileBase file){if (ModelState.IsValid){// Upload our filebook.Thumbnail = FileUpload.UploadFile(file);db.Books.Add(book);db.SaveChanges();return RedirectToAction("Index"); }return View(book);}//// GET: /Books/Edit/5public ActionResult Edit(int id){Book book = db.Books.Find(id);return View(book);}//// POST: /Books/Edit/5[HttpPost]public ActionResult Edit(Book book, HttpPostedFileBase file){if (ModelState.IsValid){// Delete old fileFileUpload.DeleteFile(book.Thumbnail);// Upload our filebook.Thumbnail = FileUpload.UploadFile(file);db.Entry(book).State = EntityState.Modified;db.SaveChanges();return RedirectToAction("Index");}return View(book);}//// GET: /Books/Delete/5public ActionResult Delete(int id){Book book = db.Books.Find(id);return View(book);}//// POST: /Books/Delete/5[HttpPost, ActionName("Delete")]public ActionResult DeleteConfirmed(int id){ Book book = db.Books.Find(id);// Delete old fileFileUpload.DeleteFile(book.Thumbnail);db.Books.Remove(book);db.SaveChanges();return RedirectToAction("Index");}protected override void Dispose(bool disposing){db.Dispose();base.Dispose(disposing);}}}
最后book/index view需要更新去初始化jQuery的自動完成。要做的第一件事是使用@節標記,包括必要的JavaScript和CSS文件。接下來,以前創建的搜索文本框更新設置一個鍵的IDwordSearch。
最后,JavaScript代碼添加在視圖的底部去在搜索文本框上建立自動完成功能。此JavaScript是有意添加在view的底部,以確保完全呈現給用戶,因為在用戶的電腦上建立數據是一項工作,Javascript處理可能會“堵塞”頁面加載。
(譯者:先呈現數據再執行javascript,js不是像傳統那樣放在head標簽里)
這取決于結果的數量。代碼如下:
@model PagedList.IPagedList<MvcApplication.Models.Book>@if (IsAjax){Layout = null;}@section JavascriptAndCSS {<link rel="stylesheet" href="@Url.Content(" rel="external nofollow" rel="external nofollow" ~/Content/jquery.autocomplete.css")" type="text/css" /><script type="text/javascript" src="@Url.Content("~/Scripts/jquery.autocomplete.js")"></script><script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>}<h2>@MvcApplication4.Resources.Resource1.BookIndexTitle</h2><p>@Html.ActionLink("Create New", "Create")</p><p>Show:@if (ViewBag.CurrentFilter != ""){@Ajax.ActionLink("All", "Index", new { sortOrder = ViewBag.CurrentSortOrder, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })}else{@:All} | @if (ViewBag.CurrentFilter != "NewReleases"){@Ajax.ActionLink("New Releases", "Index", new { filter = "NewReleases", sortOrder = ViewBag.CurrentSortOrder, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })}else{@:New Releases} | @if (ViewBag.CurrentFilter != "ComingSoon"){@Ajax.ActionLink("Coming Soon", "Index", new { filter = "ComingSoon", sortOrder = ViewBag.CurrentSortOrder, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })}else{@:Coming Soon}</p>@using (Html.BeginForm()){@:Search: @Html.TextBox("Keyword", (string)ViewBag.CurrentKeyword, new { id = "KeywordSearch" }) <input type="submit" value="Search" />}@Html.Partial("_Paging")<table><tr><th>@Ajax.ActionLink("Title", "Index", new { sortOrder = ViewBag.TitleSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })</th><th>@Ajax.ActionLink("Isbn", "Index", new { sortOrder = ViewBag.IsbnSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })</th><th>Summary</th><th>@Ajax.ActionLink("Author", "Index", new { sortOrder = ViewBag.AuthorSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })</th><th>Thumbnail</th><th>@Ajax.ActionLink("Price", "Index", new { sortOrder = ViewBag.PriceSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })</th><th>@Ajax.ActionLink("Published", "Index", new { sortOrder = ViewBag.PublishedSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" })</th><th></th></tr>@foreach (var item in Model){<tr><td>@Html.DisplayFor(modelItem => item.Title)</td><td>@Html.DisplayFor(modelItem => item.Isbn)</td><td>@Html.DisplayFor(modelItem => item.Summary)</td><td>@Html.DisplayFor(modelItem => item.Author)</td><td>@Html.DisplayFor(modelItem => item.Thumbnail)</td><td>@Html.DisplayFor(modelItem => item.Price)</td><td>@Html.DisplayFor(modelItem => item.Published)</td><td>@Html.ActionLink("Edit", "Edit", new { id = item.ID }) |@Html.ActionLink("Details", "Details", new { id = item.ID }) |@Html.ActionLink("Delete", "Delete", new { id = item.ID })</td></tr>}</table>@Html.Partial("_Paging")<script type="text/javascript">$(document).ready(function () {var data = "@ViewBag.BookTitles".split("|");$("#KeywordSearch").autocomplete(data);});</script>
為了實施選項2,一個Ajax搜索,而不是傳遞數據數組到自動完成函數,您可以傳遞一個URL。URL將需要接受查詢字符串變量:q。這包含用戶輸入的搜索值。這將用于執行書本上包含部分匹配的搜索,并返回以分隔符分隔的字符串。JQuery文檔中含有較多的這樣的成品例子,也有其他的例子,去更新的輸出結果(可能包括書的封面的縮略圖)。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答