您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

教程:使用 .NET SDK 为搜索结果添加分页功能Tutorial: Add paging to search results using the .NET SDK

了解如何实现这两个不同的分页系统,其中第一个基于页码,第二个基于无限滚动。Learn how to implement two different paging systems, the first based on page numbers and the second on infinite scrolling. 这两个分页系统都得到广泛使用,请根据要用于结果的用户体验选择合适的系统。Both systems of paging are widely used, and selecting the right one depends on the user experience you would like with the results.

本教程介绍如何执行下列操作:In this tutorial, learn how to:

  • 使用带编号的分页来扩展应用Extend your app with numbered paging
  • 使用无限滚动来扩展应用Extend your app with infinite scrolling

概述Overview

本教程将分页系统叠加到创建第一个搜索应用教程中所述的先前创建的项目中。This tutorial overlays a paging system into a previously created project described in the Create your first search app tutorial.

可以在以下项目中找到将在本教程中开发的代码的完成版本:Finished versions of the code that you will develop in this tutorial can be found in the following projects:

先决条件Prerequisites

本教程已更新,以便使用 Azure.Search.Documents(版本 11)包。This tutorial has been updated to use the Azure.Search.Documents (version 11) package. 有关 .NET SDK 的早期版本,请参阅 Microsoft.Azure.Search(版本 10)代码示例For an earlier version of the .NET SDK, see Microsoft.Azure.Search (version 10) code sample.

使用带编号的分页来扩展应用Extend your app with numbered paging

带编号的分页是主商业 Web 搜索引擎和许多其他搜索网站选定的分页系统。Numbered paging is the paging system of choice for the main commercial web search engines and many other search websites. 通常除了一系列实际页码,带编号的分页还包含“上一页”和“下一页”选项。Numbered paging typically includes a "next" and "previous" option in addition to a range of actual page numbers. 可能还会提供“第一页”和“最后一页”选项。Also a "first page" and "last page" option might also be available. 通过这些选项,用户肯定能控制基于页面的结果导航。These options certainly give a user control over navigating through page-based results.

在本教程中,你将添加一个系统,其中包含“第一页”、“上一页”、“下一页”和“最后一页”选项,且其页码不从 1 开始,而是显示与用户所在的当前页面相邻的页码(例如,如果用户正在查看第 10 页,则可能会显示第 8、9、10、11 和 12 页)。In this tutorial, you will add a system that includes first, previous, next, and last options, along with page numbers that do not start from 1, but instead surround the current page the user is on (so, for example, if the user is looking at page 10, perhaps page numbers 8, 9, 10, 11, and 12 are displayed).

该系统将足够灵活,能在全局变量中设置可见页码的编号。The system will be flexible enough to allow the number of visible page numbers to be set in a global variable.

该系统会将最右侧和最右侧的页码视为特殊值,这表示它们将触发对所示页码范围的更改。The system will treat the left-most and right-most page number buttons as special, meaning they will trigger changing the range of page numbers displayed. 例如,如果显示页码 8、9、10、11 和 12,而用户单击页码 8,则显示的页码范围更改为 6、7、8、9 和 10。For example, if page numbers 8, 9, 10, 11 and 12 are displayed, and the user clicks on 8, then the range of page numbers displayed changes to 6, 7, 8, 9, and 10. 如果用户选择页码 12,也按类似方式向右移。And there is a similar shift to the right if they selected 12.

向模型添加分页字段Add paging fields to the model

打开基本搜索页面解决方案。Have the basic search page solution open.

  1. 打开 SearchData.cs 模型文件。Open the SearchData.cs model file.

  2. 添加全局变量以支持分页。Add global variables to support pagination. 在 MVC 中,全局变量是在其自己的静态类中声明的。In MVC, global variables are declared in their own static class. ResultsPerPage 会设置每一页的结果数 。ResultsPerPage sets the number of results per page. MaxPageRange 确定视图上可见页码的编号 。MaxPageRange determines the number of visible page numbers on the view. “PageRangeDelta”决定在选中最左侧或最右侧的页码时,页面应向左或向右偏移多少页。PageRangeDelta determines how many pages should be shifted left or right, when the left-most or right-most page number is selected. 通常,后面这个数字大约是 MaxPageRange 的 1/2 。Typically this latter number is around half of MaxPageRange. 将以下代码添加到命名空间。Add the following code into the namespace.

    public static class GlobalVariables
    {
        public static int ResultsPerPage
        {
            get
            {
                return 3;
            }
        }
        public static int MaxPageRange
        {
            get
            {
                return 5;
            }
        }
    
        public static int PageRangeDelta
        {
            get
            {
                return 2;
            }
        }
    }
    

    提示

    如果在屏幕更小的设备(例如笔记本电脑)上运行此项目,请考虑将 ResultsPerPage 更改为 2 。If you are running this project on a device with a smaller screen, such as a laptop, then consider changing ResultsPerPage to 2.

  3. 将分页属性添加到“SearchData”类,“searchText”属性的后面 。Add paging properties to the SearchData class, after the searchText property.

    // The current page being displayed.
    public int currentPage { get; set; }
    
    // The total number of pages of results.
    public int pageCount { get; set; }
    
    // The left-most page number to display.
    public int leftMostPage { get; set; }
    
    // The number of page numbers to display - which can be less than MaxPageRange towards the end of the results.
    public int pageRange { get; set; }
    
    // Used when page numbers, or next or prev buttons, have been selected.
    public string paging { get; set; }
    

将分页选项表添加到视图Add a table of paging options to the view

  1. 打开 index.cshtml 文件,再添加以下代码,使其紧跟在结尾 </body> 标记的前面。Open the index.cshtml file, and add the following code right before the closing </body> tag. 这个新代码会显示分页选项表,其中包含“第一页”、“上一页”、1、2、3、4、5、“下一页”和“最后一页”。This new code presents a table of paging options: first, previous, 1, 2, 3, 4, 5, next, last.

    @if (Model != null && Model.pageCount > 1)
    {
    // If there is more than one page of results, show the paging buttons.
    <table>
        <tr>
            <td>
                @if (Model.currentPage > 0)
                {
                    <p class="pageButton">
                        @Html.ActionLink("|<", "Page", "Home", new { paging = "0" }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">|&lt;</p>
                }
            </td>
    
            <td>
                @if (Model.currentPage > 0)
                {
                    <p class="pageButton">
                        @Html.ActionLink("<", "PageAsync", "Home", new { paging = "prev" }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">&lt;</p>
                }
            </td>
    
            @for (var pn = Model.leftMostPage; pn < Model.leftMostPage + Model.pageRange; pn++)
            {
                <td>
                    @if (Model.currentPage == pn)
                    {
                        // Convert displayed page numbers to 1-based and not 0-based.
                        <p class="pageSelected">@(pn + 1)</p>
                    }
                    else
                    {
                        <p class="pageButton">
                            @Html.ActionLink((pn + 1).ToString(), "PageAsync", "Home", new { paging = @pn }, null)
                        </p>
                    }
                </td>
            }
    
            <td>
                @if (Model.currentPage < Model.pageCount - 1)
                {
                    <p class="pageButton">
                        @Html.ActionLink(">", "PageAsync", "Home", new { paging = "next" }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">&gt;</p>
                }
            </td>
    
            <td>
                @if (Model.currentPage < Model.pageCount - 1)
                {
                    <p class="pageButton">
                        @Html.ActionLink(">|", "PageAsync", "Home", new { paging = Model.pageCount - 1 }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">&gt;|</p>
                }
            </td>
        </tr>
    </table>
    }
    

    我们使用 HTML 表来保持内容井然有序。We use an HTML table to align things neatly. 但是,所有操作都来自 @Html.ActionLink 语句,每个语句使用通过不同条目创建的新模型将控制器调用到之前添加的 paging 属性中 。However all the action comes from the @Html.ActionLink statements, each calling the controller with a new model created with different entries to the paging property we added earlier.

    “第一页”和“最后一页”选项不会发送“first”和“last”之类的字符串,而是发送正确的页码。The first and last page options do not send strings such as "first" and "last", but instead send the correct page numbers.

  2. 将分页类添加到 hotels.css 文件中的 HTML 样式列表。Add paging classes to the list of HTML styles in the hotels.css file. 这里的“pageSelected”类用于在页码列表中标识当前页(通过对页码应用粗体格式)。The pageSelected class is there to identify the current page (by applying a bold format to the page number) in the list of page numbers.

    .pageButton {
        border: none;
        color: darkblue;
        font-weight: normal;
        width: 50px;
    }
    
    .pageSelected {
        border: none;
        color: black;
        font-weight: bold;
        width: 50px;
    }
    
    .pageButtonDisabled {
        border: none;
        color: lightgray;
        font-weight: bold;
        width: 50px;
    }
    

将 Page 操作添加到控制器Add a Page action to the controller

  1. 打开“HomeController.cs”文件,然后添加“PageAsync”操作。Open the HomeController.cs file, and add the PageAsync action. 此操作会响应所选的任何页码选项。This action responds to any of the page options selected.

    public async Task<ActionResult> PageAsync(SearchData model)
    {
        try
        {
            int page;
    
            switch (model.paging)
            {
                case "prev":
                    page = (int)TempData["page"] - 1;
                    break;
    
                case "next":
                    page = (int)TempData["page"] + 1;
                    break;
    
                default:
                    page = int.Parse(model.paging);
                    break;
            }
    
            // Recover the leftMostPage.
            int leftMostPage = (int)TempData["leftMostPage"];
    
            // Recover the search text and search for the data for the new page.
            model.searchText = TempData["searchfor"].ToString();
    
            await RunQueryAsync(model, page, leftMostPage);
    
            // Ensure Temp data is stored for next call, as TempData only stores for one call.
            TempData["page"] = (object)page;
            TempData["searchfor"] = model.searchText;
            TempData["leftMostPage"] = model.leftMostPage;
        }
    
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "2" });
        }
        return View("Index", model);
    }
    

    由于第三个参数(我们将稍作介绍),RunQueryAsync 方法现将显示一个语法错误 。The RunQueryAsync method will now show a syntax error, because of the third parameter, which we will come to in a bit.

    备注

    对“TempData”的调用会在临时存储中存储一个值(一个对象),虽然此存储仅保留一个调用的时长 。The calls to TempData store a value (an object) in temporary storage, though this storage persists for only one call. 如果将某些内容存储在临时数据中,则可将其用于下一次对控制器操作的调用,但在此之后的调用中,这些内容肯定会消失。If we store something in temporary data, it will be available for the next call to a controller action, but will most definitely be gone by the call after that. 由于保留期很短,因此我们会在每次调用“PageAsync”时,将搜索文本和分页属性重新存储到临时存储中。Because of this short lifespan, we store the search text and paging properties back in temporary storage each and every call to PageAsync.

  2. 更新“Index(model)”操作以存储临时变量,并将最左侧的页面参数添加到“RunQueryAsync”调用中 。Update the Index(model) action to store temporary variables, and to add the left-most page parameter to the RunQueryAsync call.

    public async Task<ActionResult> Index(SearchData model)
    {
        try
        {
            // Ensure the search string is valid.
            if (model.searchText == null)
            {
                model.searchText = "";
            }
    
            // Make the search call for the first page.
            await RunQueryAsync(model, 0, 0);
    
            // Ensure temporary data is stored for the next call.
            TempData["page"] = 0;
            TempData["leftMostPage"] = 0;
            TempData["searchfor"] = model.searchText;
        }
    
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "1" });
        }
        return View(model);
    }
    
  3. 在上一课中引入的“RunQueryAsync”方法需要修改才能解决语法错误。The RunQueryAsync method, introduced in the previous lesson, needs modification to resolve the syntax error. 我们使用 SearchOptions 类的“Skip”、“Size”和“IncludeTotalCount”字段,从“Skip”设置开始,以请求仅相当于一页内容的结果 。We use the Skip, Size, and IncludeTotalCount fields of the SearchOptions class to request only one page worth of results, starting at the Skip setting. 我们还需要为视图计算分页变量。We also need to calculate the paging variables for our view. 将整个方法替换为以下代码。Replace the entire method with the following code.

    private async Task<ActionResult> RunQueryAsync(SearchData model, int page, int leftMostPage)
    {
        InitSearch();
    
        var options = new SearchOptions
        {
            // Skip past results that have already been returned.
            Skip = page * GlobalVariables.ResultsPerPage,
    
            // Take only the next page worth of results.
            Size = GlobalVariables.ResultsPerPage,
    
            // Include the total number of results.
            IncludeTotalCount = true
        };
    
        // Add fields to include in the search results.
        options.Select.Add("HotelName");
        options.Select.Add("Description");
    
        // For efficiency, the search call should be asynchronous, so use SearchAsync rather than Search.
        model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);
    
        // This variable communicates the total number of pages to the view.
        model.pageCount = ((int)model.resultList.TotalCount + GlobalVariables.ResultsPerPage - 1) / GlobalVariables.ResultsPerPage;
    
        // This variable communicates the page number being displayed to the view.
        model.currentPage = page;
    
        // Calculate the range of page numbers to display.
        if (page == 0)
        {
            leftMostPage = 0;
        }
        else if (page <= leftMostPage)
        {
            // Trigger a switch to a lower page range.
            leftMostPage = Math.Max(page - GlobalVariables.PageRangeDelta, 0);
        }
        else if (page >= leftMostPage + GlobalVariables.MaxPageRange - 1)
        {
            // Trigger a switch to a higher page range.
            leftMostPage = Math.Min(page - GlobalVariables.PageRangeDelta, model.pageCount - GlobalVariables.MaxPageRange);
        }
        model.leftMostPage = leftMostPage;
    
        // Calculate the number of page numbers to display.
        model.pageRange = Math.Min(model.pageCount - leftMostPage, GlobalVariables.MaxPageRange);
    
        return View("Index", model);
    }
    
  4. 最后,对视图进行小幅更改。Finally, make a small change to the view. “resultList.Results.TotalCount”变量现将包含在一个页面中返回的结果数(在本例中为 3),而不是总数。The variable resultList.Results.TotalCount will now contain the number of results returned in one page (3 in our example), not the total number. 由于我们将“IncludeTotalCount”设置为了 true,因此“resultList.TotalCount”变量现包含结果总数 。Because we set the IncludeTotalCount to true, the variable resultList.TotalCount now contains the total number of results. 所以,要找到结果数在视图中显示的位置,并将其更改为以下代码。So locate where the number of results is displayed in the view, and change it to the following code.

    // Show the result count.
    <p class="sampleText">
        @Model.resultList.TotalCount Results
    </p>
    
    var results = Model.resultList.GetResults().ToList();
    
    @for (var i = 0; i < results.Count; i++)
    {
        // Display the hotel name and description.
        @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
        @Html.TextArea($"desc{1}", results[i].Document.Description, new { @class = "box2" })
    }
    

    备注

    将“IncludeTotalCount”设置为 true 时,会对性能造成轻微的影响,因为此总数需要由 Azure 认知搜索计算。There is a minor performance hit when setting IncludeTotalCount to true, as this total needs to be calculated by Azure Cognitive Search. 使用复杂数据集时,会出现警告,指示返回的值是一个近似值。With complex data sets, there is a warning that the value returned is an approximation. 由于酒店搜索语料库很小,因此该值将是准确的。Because the hotel search corpus is small, it will be accurate.

编译并运行应用Compile and run the app

现在,选择“启动但不调试”(或按 F5 键) 。Now select Start Without Debugging (or press the F5 key).

  1. 搜索返回大量结果(例如“wifi”)的字符串。Search on a string that returns plenty of results (such as "wifi"). 能否通过页面熟练地浏览结果?Can you page neatly through the results?

    带编号的分页显示“泳池”结果

  2. 请尝试单击最右侧页码,然后单击最左侧页码。Try clicking on the right-most, and later, left-most page numbers. 页码是否适当调整,将你所在的页面居中显示?Do the page numbers adjust appropriately to center the page you are on?

  3. “第一页”和“最后一页”选项是否有用?Are the "first" and "last" options useful? 某些商业搜索引擎使用这些选项,而另一些则不使用。Some commercial search engines use these options, and others do not.

  4. 请转到结果的最后一页。Go to the last page of results. 最后一页是唯一一个所含结果数可能少于 ResultsPerPage 结果数的页面 。The last page is the only page that may contain less than ResultsPerPage results.

    检查“wifi”的最后一页

  5. 键入“城镇”,然后单击“搜索”。Type in "town", and click search. 如果结果少于一页,则不会显示分页选项。No paging options are displayed if the results are fewer than one page.

    搜索“城镇”

保存此项目然后继续转到下一节,以了解另一种分页形式。Save this project and continue to the next section for an alternative form of paging.

使用无限滚动来扩展应用Extend your app with infinite scrolling

当用户将垂直滚动条滚动到正在显示的结果的最后,会触发无限滚动。Infinite scrolling is triggered when a user scrolls a vertical scroll bar to the last of the results being displayed. 在此情况下,对搜索服务的调用是针对下一页结果进行的。In this event, a call to the search service is made for the next page of results. 如果没有更多结果,则不返回任何内容,且垂直滚动条保持不变。If there are no more results, nothing is returned and the vertical scroll bar does not change. 如有更多结果,这些结果会追加到当前页面上,而滚动条会更改,显示还有更多结果可用。If there are more results, they are appended to the current page, and the scroll bar changes to show that more results are available.

需要注意的一点是,当前页面不会被替换,而是会扩展以显示其他结果。An important point to note is that the current page is not replaced, but rather extended to show the additional results. 用户可始终滚回到搜索的第一批结果。A user can always scroll back up to the first results of the search.

要实现无限滚动,首先要在添加任何页码滚动元素之前处理项目。To implement infinite scrolling, let's start with the project before any of the page number scrolling elements were added. 在 GitHub 上,这是 FirstAzureSearchApp 解决方案。On GitHub, this is the FirstAzureSearchApp solution.

向模型添加分页字段Add paging fields to the model

  1. 首先,向 SearchData.cs 模型文件中的 SearchData 类添加 paging 属性 。First, add a paging property to the SearchData class (in the SearchData.cs model file).

    // Record if the next page is requested.
    public string paging { get; set; }
    

    此变量是一个字符串;如果应发送下一页结果,或者这些结果对搜索的第一页而言为 null,则该字符串包含“next”。This variable is a string, which holds "next" if the next page of results should be sent, or be null for the first page of a search.

  2. 在同一文件且在命名空间中,添加具有一个属性的全局变量类。In the same file, and within the namespace, add a global variable class with one property. 在 MVC 中,全局变量是在其自己的静态类中声明的。In MVC, global variables are declared in their own static class. ResultsPerPage 会设置每一页的结果数 。ResultsPerPage sets the number of results per page.

    public static class GlobalVariables
    {
        public static int ResultsPerPage
        {
            get
            {
                return 3;
            }
        }
    }
    

将垂直滚动条添加到视图Add a vertical scroll bar to the view

  1. 找到 index.cshtml 文件中显示结果的部分(它以 @if (Model != null) 开头) 。Locate the section of the index.cshtml file that displays the results (it starts with the @if (Model != null)).

  2. 将此部分替换为以下代码。Replace the section with the code below. 新的 <div> 部分是围绕应可滚动的区域的,并添加了 overflow-y 属性和对名为“scrolled()”等的 onscroll 函数的调用 。The new <div> section is around the area that should be scrollable, and adds both an overflow-y attribute and a call to an onscroll function called "scrolled()", like so.

    @if (Model != null)
    {
        // Show the result count.
        <p class="sampleText">
            @Model.resultList.TotalCount Results
        </p>
    
        var results = Model.resultList.GetResults().ToList();
    
        <div id="myDiv" style="width: 800px; height: 450px; overflow-y: scroll;" onscroll="scrolled()">
    
            <!-- Show the hotel data. -->
            @for (var i = 0; i < results.Count; i++)
            {
                // Display the hotel name and description.
                @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
                @Html.TextArea($"desc{i}", results[i].Document.Description, new { @class = "box2" })
            }
    
  3. 在循环的正下文,在 </div> 标记的后面添加 scrolled 函数 。Directly underneath the loop, after the </div> tag, add the scrolled function.

    <script>
        function scrolled() {
            if (myDiv.offsetHeight + myDiv.scrollTop >= myDiv.scrollHeight) {
                $.getJSON("/Home/NextAsync", function (data) {
                    var div = document.getElementById('myDiv');
    
                    // Append the returned data to the current list of hotels.
                    for (var i = 0; i < data.length; i += 2) {
                        div.innerHTML += '\n<textarea class="box1">' + data[i] + '</textarea>';
                        div.innerHTML += '\n<textarea class="box2">' + data[i + 1] + '</textarea>';
                    }
                });
            }
        }
    </script>
    

    上述脚本中的“if”语句会测试用户是否已滚动到垂直滚动条的底部。The if statement in the script above tests whether the user has scrolled to the bottom of the vertical scroll bar. 如果已滚动到底部,则会对名为“NextAsync”的操作调用“Home”控制器 。If they have, a call to the Home controller is made to an action called NextAsync. 无需控制器提供任何其他信息,它将返回数据的下一页。No other information is needed by the controller, it will return the next page of data. 此数据随后按原始页面相同的 HTML 样式设置格式。This data is then formatted using identical HTML styles as the original page. 如果未返回任何结果,则不追加任何内容且原内容保持不变。If no results are returned, nothing is appended and things stay as they are.

处理 Next 操作Handle the Next action

只需要向控制器发送下列三个操作:应用的第一次运行(调用 Index())、用户的第一次搜索(调用 Index(model)),然后是通过 Next(model) 进行后续调用来查找更多结果 。There are only three actions that need to be sent to the controller: the first running of the app, which calls Index(), the first search by the user, which calls Index(model), and then the subsequent calls for more results via Next(model).

  1. 打开主控制器文件,从原始脚本中删除 RunQueryAsync 方法 。Open the home controller file and delete the RunQueryAsync method from the original tutorial.

  2. 将 Index(model) 操作替换为以下代码 。Replace the Index(model) action with the following code. 它现可处理“分页”字段(为 null 时),或者设置为“下一步”并处理对 Azure 认知搜索的调用 。It now handles the paging field when it is null, or set to "next", and handles the call to Azure Cognitive Search.

    public async Task<ActionResult> Index(SearchData model)
    {
        try
        {
            InitSearch();
    
            int page;
    
            if (model.paging != null && model.paging == "next")
            {
                // Increment the page.
                page = (int)TempData["page"] + 1;
    
                // Recover the search text.
                model.searchText = TempData["searchfor"].ToString();
            }
            else
            {
                // First call. Check for valid text input.
                if (model.searchText == null)
                {
                    model.searchText = "";
                }
                page = 0;
            }
    
            // Setup the search parameters.
            var options = new SearchOptions
            {
                SearchMode = SearchMode.All,
    
                // Skip past results that have already been returned.
                Skip = page * GlobalVariables.ResultsPerPage,
    
                // Take only the next page worth of results.
                Size = GlobalVariables.ResultsPerPage,
    
                // Include the total number of results.
                IncludeTotalCount = true
            };
    
            // Specify which fields to include in results.
            options.Select.Add("HotelName");
            options.Select.Add("Description");
    
            // For efficiency, the search call should be asynchronous, so use SearchAsync rather than Search.
            model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);               
    
            // Ensure TempData is stored for the next call.
            TempData["page"] = page;
            TempData["searchfor"] = model.searchText;
        }
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "1" });
        }
    
        return View("Index", model);
    }
    

    与带编号的分页方法类似,我们使用“Skip”和“Size”搜索设置来请求仅返回所需的数据 。Similar to the numbered paging method, we use the Skip and Size search settings to request just the data we need is returned.

  3. 向主控制器添加“NextAsync”操作。Add the NextAsync action to the home controller. 请注意它返回列表的方式:每家酒店会向列表添加两个元素,即酒店名称和酒店说明。Notice how it returns a list, each hotel adding two elements to the list: a hotel name and a hotel description. 设置此格式,使其与 scrolled 函数对视图中返回的数据的使用相一致 。This format is set to match the scrolled function's use of the returned data in the view.

    public async Task<ActionResult> NextAsync(SearchData model)
    {
        // Set the next page setting, and call the Index(model) action.
        model.paging = "next";
        await Index(model).ConfigureAwait(false);
    
        // Create an empty list.
        var nextHotels = new List<string>();
    
        // Add a hotel name, then description, to the list.
        await foreach (var searchResult in model.resultList.GetResultsAsync())
        {
            nextHotels.Add(searchResult.Document.HotelName);
            nextHotels.Add(searchResult.Document.Description);
        }
    
        // Rather than return a view, return the list of data.
        return new JsonResult(nextHotels);
    }
    
  4. 如果在“List<string>”上出现语法错误,请将以下 using 指令添加到控制器文件的开头 。If you get a syntax error on List<string>, add the following using directive to the head of the controller file.

    using System.Collections.Generic;
    

编译并运行项目Compile and run your project

现在,选择“启动但不调试”(或按 F5 键) 。Now select Start Without Debugging (or press the F5 key).

  1. 输入一个会返回更多结果的词(例如“泳池”),然后测试垂直滚动条。Enter a term that will give plenty of results (such as "pool") and then test the vertical scroll bar. 它会触发新的一页结果吗?Does it trigger a new page of results?

    无限滚动查看“泳池”结果

    提示

    要确保第一页上显示滚动条,第一页结果必须稍微超过当前显示这些结果的区域的高度。To ensure that a scroll bar appears on the first page, the first page of results must slightly exceed the height of the area they are being displayed in. 在我们的示例中,.box1 具有 30 像素的高度,而 .box2 具有 100 像素的高度,其下边距为 24 像素 。In our example .box1 has a height of 30 pixels, .box2 has a height of 100 pixels and a bottom margin of 24 pixels. 因此,每个条目都使用 154 像素。So each entry uses 154 pixels. 三个条目占用的像素将为 3 x 154 = 462 个。Three entries will take up 3 x 154 = 462 pixels. 要确保显示垂直滚动条,必须将显示区域的高度设置为小于 462 像素(甚至可使用 461 像素)。To ensure that a vertical scroll bar appears, a height to the display area must be set that is smaller than 462 pixels, even 461 works. 此问题只出现在第一页上,后续页面肯定会显示滚动条。This issue only occurs on the first page, after that a scroll bar is sure to appear. 要更新的行为 <div id="myDiv" style="width:800px; height:450px; overflow-y: scroll;" onscroll="scrolled()">The line to update is: <div id="myDiv" style="width: 800px; height: 450px; overflow-y: scroll;" onscroll="scrolled()">.

  2. 向下滚动一直到结果的底部。Scroll down all the way to the bottom of the results. 请注意所有信息现在一个视图页面上的显示方式。Notice how all information is now on the one view page. 可一直滚动回顶部,而不触发任何服务器调用。You can scroll all the way back to the top without triggering any server calls.

更复杂的无限滚动系统可能会使用鼠标滚轮或其他类似机制来触发新页结果的加载。More sophisticated infinite scrolling systems might use the mouse wheel, or similar other mechanism, to trigger the loading of a new page of results. 在这些教程中,我们将不再进一步执行无限滚动,尽管它具有一定的魅力,因为它能避免额外的鼠标点击,而你可能想要进一步研究其他选项。We will not be taking infinite scrolling any further in these tutorials, but it has a certain charm to it as it avoids extra mouse clicks, and you might want to investigate other options further.

要点Takeaways

请考虑此项目中的以下要点:Consider the following takeaways from this project:

  • 带编号的分页非常适合结果顺序比较随意的搜索,这意味着后续页面上可能有用户感兴趣的内容。Numbered paging is useful for searches where the order of the results is somewhat arbitrary, meaning there may well be something of interest to your users on the later pages.
  • 如果结果的顺序非常重要,则无限滚动很适用。Infinite scrolling is useful when the order of results is particularly important. 例如,如果结果是按到目标城市中心的距离进行排序的。For example, if the results are ordered on the distance from the center of a destination city.
  • 通过带编号的分页,可更好地进行导航。Numbered paging allows for better navigation. 例如,用户可能记得第 6 页上有一个有趣的结果,但是无限滚动中没有此类简单的参考。For example, a user can remember that an interesting result was on page 6, whereas no such easy reference exists in infinite scrolling.
  • 无限滚动有一种简单的魔力,你只需上下滚动而不必单击页码。Infinite scrolling has an easy appeal, scrolling up and down with no page numbers to click on.
  • 无限滚动的一项重要功能是,结果会追加到现有页面上而不是替代该页面,这很有效。A key feature of infinite scrolling is that results are appended to an existing page, not replacing that page, which is efficient.
  • 临时存储仅保存一个调用的时长,需要重置以经受住额外调用。Temporary storage persists for only one call, and needs to be reset to survive additional calls.

后续步骤Next steps

分页是搜索体验的基础。Paging is fundamental to a search experience. 在详细了解分页后,下一步是添加预先输入搜索,进一步改进用户体验。With paging well covered, the next step is to improve the user experience further by adding type-ahead searches.