Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Naučte se implementovat dva různé stránkovací systémy, první na základě čísel stránek a druhý při nekonečném posouvání. Oba systémy stránkování se široce používají a výběr toho správného závisí na uživatelském zážitku, který chcete mít s výsledky.
V tomto kurzu se naučíte:
- Rozšiřte svou aplikaci číslovaným stránkováním
- Rozšíření aplikace nekonečným posouváním
Přehled
Tento tutoriál integruje stránkovací systém do již vytvořeného projektu, který je popsán v Vytvoření první vyhledávací aplikace.
Dokončené verze kódu, který budete vyvíjet v tomto kurzu, najdete v následujících projektech:
Požadavky
- 1-basic-search-page (GitHub) projekt. Tento projekt může být buď vaše vlastní verze sestavená z předchozího kurzu, nebo kopie z GitHubu.
Rozšiřte svou aplikaci o číslované stránkování
Číslované stránkování je preferovaným systémem stránkování pro hlavní komerční vyhledávače a mnoho dalších vyhledávacích webů. Číslované stránkování obvykle obsahuje kromě rozsahu skutečných čísel stránek možnosti "další stránka" a "předchozí stránka". Může být také dostupná možnost "první stránka" a "poslední stránka". Tyto možnosti jistě dávají uživateli kontrolu nad procházením výsledků založených na stránkách.
V tomto kurzu přidáte systém, který obsahuje první, předchozí, další a poslední možnosti, spolu s čísly stránek, které nezačínají od 1, ale místo toho obklopují aktuální stránku, na které je uživatel (takže pokud se například uživatel dívá na stránku 10, může se zobrazit čísla stránek 8, 9, 10, 11 a 12).
Systém bude dostatečně flexibilní, aby bylo možné nastavit počet viditelných čísel stránek v globální proměnné.
Systém bude zacházet s tlačítky čísel stránek nejvíce vlevo a zprava jako se speciálními tlačítky, což znamená, že se aktivuje změna rozsahu zobrazených čísel stránek. Pokud se například zobrazí čísla stránek 8, 9, 10, 11 a 12 a uživatel klikne na číslo 8, změní se rozsah zobrazených čísel stránek na 6, 7, 8, 9 a 10. A existuje podobný posun vpravo, pokud vyberou 12.
Přidání stránkovacího pole do modelu
Mějte základní řešení vyhledávací stránky otevřené.
Otevřete soubor modelu SearchData.cs.
Přidejte globální proměnné pro podporu stránkování. V MVC jsou globální proměnné deklarovány ve své vlastní statické třídě. ResultsPerPage nastaví počet výsledků na stránku. MaxPageRange určuje počet viditelných čísel stránek v zobrazení. PageRangeDelta určuje, kolik stránek má být posunuto doleva nebo doprava, když je vybráno číslo stránky nejvíce zleva nebo doprava. Obvykle je toto druhé číslo přibližně polovina MaxPageRange. Do oboru názvů přidejte následující kód.
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; } } }Návod
Pokud tento projekt spouštíte na zařízení s menší obrazovkou, jako je notebook, zvažte změnu PočetVýsledkůNaStránku na 2.
Za vlastnost searchText přidejte vlastnosti stránkování do třídy SearchData.
// 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; }
Přidání tabulky možností stránkování do zobrazení
Otevřete soubor index.cshtml a před uzavírací <značku /body> přidejte následující kód. Tento nový kód představuje tabulku možností stránkování: první, předchozí, 1, 2, 3, 4, 5, další, poslední.
@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">|<</p> } </td> <td> @if (Model.currentPage > 0) { <p class="pageButton"> @Html.ActionLink("<", "PageAsync", "Home", new { paging = "prev" }, null) </p> } else { <p class="pageButtonDisabled"><</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">></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">>|</p> } </td> </tr> </table> }K zarovnání věcí používáme tabulku HTML. Všechny akce však pocházejí z @Html.ActionLink příkazů, přičemž každý volá kontrolér s novým modelem, který obsahuje různé položky ve vlastnosti stránkování, kterou jsme přidali dříve.
Možnosti první a poslední stránky neodesílají řetězce jako "první" a "poslední", ale místo toho odesílají správná čísla stránek.
Do seznamu stylů HTML v souboru hotels.css přidejte stránkovací třídy. Třída pageSelected je tam k identifikaci aktuální stránky (použitím tučného formátu na číslo stránky) v seznamu čísel stránek.
.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; }
Přidejte akci stránky do kontroléru
Otevřete soubor HomeController.cs a přidejte akci PageAsync . Tato akce reaguje na některou z vybraných možností stránky.
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); }Metoda RunQueryAsync teď zobrazí chybu syntaxe kvůli třetímu parametru, kterým se budeme zabývat za chvíli.
Poznámka:
Volání tempData ukládají hodnotu ( objekt) v dočasném úložišti, i když toto úložiště přetrvává pouze pro jedno volání. Pokud něco uložíme do dočasných dat, bude k dispozici pro další volání akce kontroléru, ale s největší pravděpodobností již nebude dostupné při následujícím volání. Kvůli této krátké životnosti ukládáme hledaný text a stránkovací vlastnosti zpět do dočasného úložiště při každém jednotlivém volání PageAsync.
Aktualizujte akci Index(model) tak, aby ukládala dočasné proměnné, a přidejte parametr stránky úplně vlevo do volání RunQueryAsync .
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); }Metoda RunQueryAsync , která byla představena v předchozí lekci, potřebuje úpravu, aby se vyřešila chyba syntaxe. Pole Skip, Size a IncludeTotalCount třídy SearchOptions používáme k vyžádání pouze jedné stránky s výsledky počínaje nastavením Skip . Musíme také vypočítat stránkovací proměnné pro naše zobrazení. Nahraďte celou metodu následujícím kódem.
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); }Nakonec proveďte malou změnu zobrazení. Proměnná resultList.Results.TotalCount teď bude obsahovat počet výsledků vrácených na jedné stránce (3 v našem příkladu), nikoli celkové číslo. Vzhledem k tomu, že includeTotalCount nastavíme na hodnotu true, proměnná resultList.TotalCount teď obsahuje celkový počet výsledků. Vyhledejte proto, kde se v zobrazení zobrazí počet výsledků, a změňte ho na následující kód.
// 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" }) }Poznámka:
Při nastavení IncludeTotalCount na hodnotu true dojde k menšímu poklesu výkonu, protože tento součet musí služba Azure Cognitive Search vypočítat. V případě složitých datových sad je upozornění, že vrácená hodnota je přibližná hodnota. Vzhledem k tomu, že hotelový vyhledávací korpus je malý, bude přesný.
Kompilace a spuštění aplikace
Teď vyberte Spustit bez ladění (nebo stiskněte klávesu F5).
Vyhledejte text, který vrací spoustu výsledků (například "wifi"). Můžete přehledně procházet výsledky?
Zkuste kliknout na čísla stránek úplně vpravo a později úplně vlevo. Upraví se čísla stránek odpovídajícím způsobem tak, aby byla zarovnána na střed stránky, na které se nacházíte?
Jsou užitečné možnosti "první" a "poslední"? Některé komerční vyhledávací weby tyto možnosti používají a jiné ne.
Přejděte na poslední stránku výsledků. Poslední stránka je jediná stránka, která může obsahovat méně než výsledky ResultsPerPage .
Zadejte "město" a klikněte na hledat. Pokud jsou výsledky menší než jedna stránka, nezobrazí se žádné možnosti stránkování.
Uložte tento projekt a pokračujte k další části pro alternativní formu stránkování.
Rozšíření aplikace nekonečným posouváním
Nekonečné posouvání se aktivuje, když uživatel posune svislý posuvník na poslední zobrazené výsledky. V tomto případě se pro další stránku výsledků vytvoří volání vyhledávací služby. Pokud žádné další výsledky nejsou, nic se nevrátí a svislý posuvník se nezmění. Pokud jsou k dispozici další výsledky, připojí se k aktuální stránce a posuvník se změní, aby se zobrazilo, že jsou k dispozici další výsledky.
Důležitým bodem k poznámce je, že aktuální stránka není nahrazena, ale spíše rozšířena tak, aby zobrazovala další výsledky. Uživatel se může vždy posunout zpět k prvním výsledkům hledání.
Abychom mohli implementovat nekonečné posouvání, začněme projektem před tím, než byly přidány některé prvky posouvání čísel stránek. Na GitHubu se jedná o řešení FirstAzureSearchApp .
Přidání stránkovacího pole do modelu
Nejprve přidejte stránkovací vlastnost do třídy SearchData (v souboru modelu SearchData.cs).
// Record if the next page is requested. public string paging { get; set; }Tato proměnná je řetězec, který obsahuje "další", pokud se má odeslat další stránka výsledků, nebo má hodnotu null pro první stránku hledání.
Do stejného souboru a v rámci oboru názvů přidejte globální třídu proměnných s jednou vlastností. V MVC jsou globální proměnné deklarovány ve své vlastní statické třídě. ResultsPerPage nastaví počet výsledků na stránku.
public static class GlobalVariables { public static int ResultsPerPage { get { return 3; } } }
Přidání svislého posuvníku do zobrazení
Vyhledejte část souboru index.cshtml, která zobrazuje výsledky (začíná @if (Model != null)).
Část nahraďte následujícím kódem. Nový <oddíl div> se nachází kolem oblasti, která by měla být posuvná, a přidává jak atribut overflow-y, tak volání funkce onscroll s názvem "scrolled()", například.
@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" }) }Přímo pod smyčkou, za <značkou /div> přidejte funkci scrolled.
<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>Příkaz if ve skriptu výše testuje, zda se uživatel posunoval do dolní části svislého posuvníku. Pokud ano, provede se volání na řadič Home k akci nazvané NextAsync. Kontroler nepotřebuje žádné další informace, vrátí další stránku dat. Tato data se pak naformátují pomocí stejných stylů HTML jako původní stránky. Pokud se nevrátí žádné výsledky, nic se nepřipojí a situace zůstane nezměněná.
Zpracovat akci ‚Next‘
Kontroleru je potřeba odeslat jenom tři akce: první spuštění aplikace, které volá Index(), první hledání uživatelem, které volá Index(model) a následné volání dalších výsledků prostřednictvím Next(model).
Otevřete soubor domovského kontroleru a odstraňte metodu RunQueryAsync z původního kurzu.
Akci Index(model) nahraďte následujícím kódem. Teď zpracovává stránkovací pole, když má hodnotu null nebo je nastaveno na další, a zpracovává volání služby 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); }Podobně jako u očíslované metody stránkování používáme nastavení hledání Skip a Size, abychom vyžádali návrat jen potřebných dat.
Přidejte akci NextAsync do domovského kontroleru. Všimněte si, jak vrátí seznam, přičemž každý hotel přidává do seznamu dva prvky: název hotelu a popis hotelu. Tento formát je nastaven tak, aby odpovídal použití vrácených dat funkce scrolled v zobrazení.
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); }Pokud se u řetězce> list< zobrazí chyba syntaxe, přidejte do hlavy souboru kontroleru následující direktivu using.
using System.Collections.Generic;
Kompilace a spuštění projektu
Teď vyberte Spustit bez ladění (nebo stiskněte klávesu F5).
Zadejte termín, který poskytne spoustu výsledků (například "pool") a pak otestujte svislý posuvník. Aktivuje se nová stránka výsledků?
Návod
Aby se zajistilo, že se na první stránce zobrazí posuvník, musí první stránka výsledků mírně překročit výšku oblasti, ve které se zobrazují. V našem příkladu .box1 má výšku 30 pixelů, .box2 má výšku 100 pixelů a dolní okraj 24 pixelů. Každá položka tedy používá 154 pixelů. Tři položky zabírají 3 x 154 = 462 pixelů. Aby se zajistilo, že se zobrazí svislý posuvník, musí být výška oblasti zobrazení nastavena na méně než 462 pixelů - i 461 pixelů funguje. K tomuto problému dochází pouze na první stránce, následně se posuvník určitě zobrazí. Řádek, který se má aktualizovat, je: <div id="myDiv" style="width: 800px; height: 450px; overflow-y: scroll;" onscroll="scrolled()">.
Posuňte se dolů až do dolní části výsledků. Všimněte si, že všechny informace jsou teď na jedné stránce zobrazení. Můžete se posouvat úplně nahoru, aniž byste aktivovaly volání serveru.
Sofistikovanější nekonečné posuvné systémy můžou k aktivaci načítání nové stránky výsledků použít kolečko myši nebo podobný mechanismus. V těchto kurzech se nedostaneme k nekonečnému posouvání, ale má své kouzlo, protože zabraňuje dalšímu klikání myší a možná budete chtít dále prozkoumat jiné možnosti.
Klíčové poznatky
Vezměte v úvahu následující poznatky z tohoto projektu:
- Číslované stránkování je užitečné pro hledání, kde je pořadí výsledků poněkud libovolné, což znamená, že na pozdějších stránkách může být něco zajímavého pro vaše uživatele.
- Nekonečné posouvání je užitečné, když je pořadí výsledků obzvláště důležité. Pokud jsou například výsledky seřazené podle vzdálenosti od centra cílového města.
- Číslované stránkování umožňuje lepší navigaci. Uživatel si například může vzpomenout, že zajímavý výsledek byl na stránce 6, zatímco v nekonečném posouvání neexistuje žádný takový snadný odkaz.
- Nekonečné posouvání je snadno atraktivní, posouvání nahoru a dolů bez nutnosti klikat na čísla stránek.
- Klíčovou funkcí nekonečného posouvání je, že výsledky jsou připojeny k existující stránce, nikoli nahrazení této stránky, což je efektivní.
- Dočasné úložiště trvá jenom pro jedno volání a je potřeba ho resetovat, aby přetrvalo další volání.
Další kroky
Stránkování je zásadní pro vyhledávání. Při dobrém pokrytí stránkování je dalším krokem zlepšení zážitku uživatele přidáním vyhledávání s našeptávačem.