• [翻译]ASP.NET MVC 3 开发的20个秘诀(十八)[20 Recipes for Programming MVC 3]:自动完成搜索


    议题

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

     

    解决方案

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

     

    讨论

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

     

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

     

    修改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会在绝大多数的页面中被使用。但是,我们新引用的AjaxjQuery并不是每页都必须要求的。

     

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

        1. 将数据发送到Javascript中进行搜索;
        2. 在用户输入时通过Ajax获取结果。

     

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

     

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

    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包含必要的JavaScriptCSS文件的引用。然后,将以前创建的搜索文本框的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文档中有完整的例子,以及其他相关的例子(可能包含返回书籍的封面的缩略图等)。

     

    参考

    Jquery.Autocomplete RenderSection 原书地址 书籍源代码

  • 相关阅读:
    浅谈Linux的内存管理机制
    [SCM]源码管理 perforce状态的检测
    轻松构建Mysql高可用集群系统
    [BuildRelease].NET代码静态检测FxCop
    Ant高级task
    Jenkins master在windows上安装
    Jenkins的Windows Slave的配置
    Jenkins的配置
    [BuildRelease]跨平台build脚本
    使用Synergy多台电脑共享键盘鼠标和剪贴板
  • 原文地址:https://www.cnblogs.com/o2ds/p/2312671.html
Copyright © 2020-2023  润新知