技术开发 频道

ASP.NET MVC 开发秘诀:自动完成搜索

        【IT168 技术】你在搜索某个具体事情的时候,可能输入会比较复杂(或需要很长时间),正当用户在试图输入正确的内容时,搜索并显示可能是用户输入的内容,让用户查找输入和搜索变的更加容易。

  解决方案

  使用JQuery自动完成插件,修改图书列表页面上的搜索功能,使用户在输入搜索时,立即显示搜索结果。

  讨论

  自动完成插件默认不包含在MVC项目的JQuery基本包中,所以,要做的第一件事是访问http://jquery.com去下载插件。这个插件包含两个必需文件,一个Javascript文件和一个CSS文件。将新下载的文件放置到“Scripts”文件夹中。将CSS文件放置到“Content”文件夹中。

  这个秘诀也会介绍关于视图渲染部分的内容。为了提高访问性能,在全局共享视图中添加Ajax和不唐突Ajax文件以及名为“main”的CSS文件,以避免每次加载不同视图时都就要加载这些文件。并在共享视图里面添加RenderSection()方法。这将允许每个视图根据具体情况在标签中添加特需的Javascript或CSS文件。

  修改Views/Shared/_Layout.cshtml,添加新方法RenderSection():

<!DOCTYPE html>
<html>   
 <head>       
 <title>@ViewBag.Title</title>       
 <link href="@Url.Content("~/Content/Site.css")"         
   rel="stylesheet" type="text/css" />        
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")"           
 type="text/javascript"></script>       
 @RenderSection("JavaScriptAndCSS", required: false)    </head>   
 <body>        
<div class="page">           
 <div id="header">              
  <div id="title">                  
  <h1>My MVC Application</h1>               
 </div>               
 <div id="logindisplay">                   
 @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">                 
   <ul id="menu">                       
 <li>@Html.ActionLink("Home",    "Index", "Home")</li>                        
<li>@Html.ActionLink("About",  "About", "Home")</li>                  
  </ul>              
  </div>          
  </div>          
  <div id="main">                @RenderBody()          
  </div>          
  <div id="footer">            </div>        
</div>   
 </body>
</html>

 

  只有主要的CSS文件以及jQuery的核心文件在共享文件中加载,因为CSS是每一页都需要的,而jQuery会在绝大多数的页面中被使用。但是,我们新引用的Ajax的jQuery并不是每页都必须要求的。

  现在我们使用自动完成插件有两种方法:

  1.将数据发送到Javascript中进行搜索;

  2.在用户输入时通过Ajax获取结果。

  以我使用的感觉来说,我发现解决方案1的自动完成方法要快很多,因为它并不需要要每次都从数据库中请求数据。然而这个解决方案限制于数据量的多少,当搜索内容过于庞大时,用户页面加载以及调用Javascript方法时都会导致加载用户计算机运行变慢或等待时间过长。经过一些错误跟踪实验,我大致可以确定这个神奇的数字大约是40000条数据。如果结果数量超过这个值就需要使用2号解决方案,否则,始终坚持方案1,因为搜索时瞬间的体验,尽量不要有轻微的延迟。

  在这个例子中,将要搜索的书籍没有超过40000上限,所以我们会选择使用方案1。现在必须对BooksController进行修改,创建一个存储书名列表的ViewBag变量。自动完成时需要有一个Javascript的数组,将这些书籍名称使用“|”字符进行分割。然后在视图中,这个字符串将会被Javascript的Split()方法转换为数组对象。修改了这个功能后,当用户完全的输入了想搜索的结果,将会返回一本书的匹配结果,用户点击该结果将会自动重定向到这本书的详情页面。

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 MvcApplication4.Models;
using MvcApplication4.Utils;
using PagedList;namespace MvcApplication4.Controllers
{    public class BooksController : Controller   
 {        private BookDBContext db = new BookDBContext();        //     
   // GET: /Books/        
public ActionResult Index(string sortOrder,    string filter, string Keyword, int page = 1)       
 {            #region ViewBag Resources               
 ...            #endregion          
  #region ViewBag Sort Params                ...         
   #endregion                       
 var books = from b in db.Books select b;                      
  #region Keyword Search          
  if (!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 switch                ...           
 #endregion            
books = books.OrderBy(sortOrder);           
 int maxRecords = 1;           
 int currentPage = page - 1;            // Get all book titles           
 ViewBag.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;        }       
 ...    }}

 

  最后,修改Books/Index视图,添加jQuery自动完成的功能。首先,是使用@section包含必要的JavaScript和CSS文件的引用。然后,将以前创建的搜索文本框的ID修改为“wordSearch”。最后,将自动完成的代码添加到自动完成搜索框的下面。这个JavaScript是有意放置在视图的底部的,根据返回结果数量的不同,这个JavaScript的“代码块(Blocking)”可能会给用户的计算机带来很多处理负载而不至于影响视图呈现。

@model PagedList.IPagedList<MvcApplication4.Models.Book>@if (IsAjax)
{    Layout = null;}@section JavascriptAndCSS
{    <link rel="stylesheet" href="   @Url.Content("~/Content/jquery.autocomplete.css")"           
 type="text/css" />  
<script src="@Url.Content(   "~/Scripts/jquery.unobtrusive-ajax.min.js")"           
 type="text/javascript">
</script>  
<script type="text/javascript" src="@Url.Content( "~/Scripts/jquery.autocomplete.js")">
 </script>}
...@using (Html.BeginForm())
{    @:Search: @Html.TextBox("Keyword",   (string)ViewBag.CurrentKeyword, new { id = "KeywordSearch" })    
<input type="submit" value="Search" />
}...
<script type="text/javascript">  
  $(document).ready(function ()
{ var data = "@ViewBag.BookTitles".split("|");  
$("#KeywordSearch").autocomplete(data);    });
</script>

  为了实现第二种方案,Ajax搜索,通过URL传递搜索参数,而不是通过自动完成方法访问数组。该URL将包含一个查询字符串变量“q”。这个变量将包含用户输入的搜索值。这将用来执行搜索与书籍的信息进行匹配,并将返回的内容使用字符分隔符进行分隔。jQuery文档中有完整的例子,以及其他相关的例子(可能包含返回书籍的封面的缩略图等)。

0
相关文章