上一篇完成了博客的主題切換,菜單和二維碼的顯示與隱藏功能,本篇繼續完成分頁查詢文章列表的數據展現。html
如今點擊頁面上的連接,都會提示錯誤消息,由於沒有找到對應的路由地址。先在Pages下建立五個文件夾:Posts、Categories、Tags、Apps、FriendLinks。前端
而後在對應的文件夾下添加Razor組件。git
Posts.razor
、根據分類查詢文章列表頁面Posts.Category.razor
、根據標籤查詢文章列表頁面Posts.Tag.razor
、文章詳情頁Post.razor
Categories.razor
Tags.razor
Apps.razor
準備將友情連接入口放在裏面FriendLinks.razor
先分別建立上面這些Razor組件,差很少除了後臺CURD的頁面就這些了,如今來逐個突破。github
無論三七二十一,先把全部頁面的路由給肯定了,指定頁面路由使用 @page
指令,官方文檔說不支持可選參數,可是能夠支持多個路由規則。json
默認先什麼都不顯示,能夠將以前的加載中圈圈寫成一個組件,供每一個頁面使用。api
在Shared文件夾添加組件Loading.razor
。緩存
<!--Loading.razor--> <div class="loader"></div>
//Posts.razor @page "/posts/" @page "/posts/page/{page:int}" @page "/posts/{page:int}" <Loading /> @code { /// <summary> /// 當前頁碼 /// </summary> [Parameter] public int? page { get; set; } }
這裏我加了三條,能夠匹配沒有page參數,帶page參數的,/posts/page/{page:int}
這個你們能夠不用加,我是用來兼容目前線上的博客路由的。總的來講能夠匹配到:/posts
、/posts/1
、/posts/page/1
這樣的路由。app
//Posts.Category.razor @page "/category/{name}" <Loading /> @code { /// <summary> /// 分類名稱參數 /// </summary> [Parameter] public string name { get; set; } }
根據分類名稱查詢文章列表頁面,name看成分類名稱參數,能夠匹配到相似於:/category/aaa
、/category/bbb
這樣的路由。框架
//Posts.Tag.razor @page "/tag/{name}" <Loading /> @code { /// <summary> /// 標籤名稱參數 /// </summary> [Parameter] public string name { get; set; } }
這個根據標籤名稱查詢文章列表頁面和上面差很少同樣,能夠匹配到:/tag/aaa
、/tag/bbb
這樣的路由。async
//Post.razor @page "/post/{year:int}/{month:int}/{day:int}/{name}" <Loading /> @code { [Parameter] public int year { get; set; } [Parameter] public int month { get; set; } [Parameter] public int day { get; set; } [Parameter] public string name { get; set; } }
文章詳情頁面的路由有點點複雜,以/post/開頭,加上年月日和當前文章的語義化名稱組成。分別添加了四個參數年月日和名稱,用來接收URL的規則,使用int來設置路由的約束,最終能夠匹配到路由:/post/2020/06/09/aaa
、/post/2020/06/9/bbb
這樣的。
//Categories.razor @page "/categories" <Loading /> //Tags.razor @page "/tags" <Loading /> //FriendLinks.razor @page "/friendlinks" <Loading />
分類、標籤、友情連接都是固定的路由,像上面這樣就很少說了,而後還剩一個Apps.razor
。
//Apps.razor @page "/apps" <div class="container"> <div class="post-wrap"> <h2 class="post-title">- Apps -</h2> <ul> <li> <a target="_blank" href="https://support.qq.com/products/75616"><h3>吐個槽_留言板</h3></a> </li> <li> <NavLink href="/friendlinks"><h3>友情連接</h3></NavLink> </li> </ul> </div> </div>
在裏面添加了一個友情連接的入口,和一個 騰訊兔小巢 的連接,歡迎你們吐槽留言噢。
如今能夠運行一下看看,點擊全部的連接都不會提示錯誤,只要路由匹配正確就會出現加載中的圈圈了。
在作文章列表的數據綁定的時候遇到了大坑,有前端開發經驗的都知道,JavaScript弱類型語言中接收json數據隨便玩,可是在Blazor中我試了下動態接受傳遞過來的JSON數據,一直報錯壓根運行不起來。因此在請求api接收數據的時候須要指定接收對象,那就好辦了我就直接引用API中的.Application.Contracts
就好了啊,可是緊接着坑又來了,目標框架對不上,引用以後也運行不起來,這裏應該是以前沒有設計好。
因而,我就想了一個折中的辦法吧,將API中的返回對象能夠用到的DTO先手動拷貝一份到Blazor項目中,後續能夠考慮將公共的返回模型作成Nuget包,方便使用。
那麼,最終就是在Blazor中添加一個Response文件夾,用來放接收對象,裏面的內容看圖:
有點傻,先這樣解決,後面在作進一步的優化吧。
將咱們複製進來的東東,在_Imports.razor
中添加引用。
//_Imports.razor @using System.Net.Http @using System.Net.Http.Json @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Meowv.Blog.BlazorApp.Shared @using Response.Base @using Response.Blog @inject HttpClient Http @inject Commons.Common Common
@inject HttpClient Http
:注入HttpClient
,用它來請求API數據。
如今有了接收對象,接下來就好辦了,來實現分頁查詢文章列表吧。
先添加三個私有變量,限制條數,就是一次加載文章的數量,總頁碼用來計算分頁,還有就是API的返回數據的接收類型參數。
/// <summary> /// 限制條數 /// </summary> private int Limit = 15; /// <summary> /// 總頁碼 /// </summary> private int TotalPage; /// <summary> /// 文章列表數據 /// </summary> private ServiceResult<PagedList<QueryPostDto>> posts;
而後當頁面初始化的時候,去加載數據,渲染頁面,由於page參數可能存在爲空的狀況,因此要考慮進去,當爲空的時候給他一個默認值1。
/// <summary> /// 初始化 /// </summary> protected override async Task OnInitializedAsync() { // 設置默認值 page = page.HasValue ? page : 1; await RenderPage(page); } /// <summary> /// 點擊頁碼從新渲染數據 /// </summary> /// <param name="page"></param> /// <returns></returns> private async Task RenderPage(int? page) { // 獲取數據 posts = await Http.GetFromJsonAsync<ServiceResult<PagedList<QueryPostDto>>>($"/blog/posts?page={page}&limit={Limit}"); // 計算總頁碼 TotalPage = (int)Math.Ceiling((posts.Result.Total / (double)Limit)); }
在初始化方法中設置默認值,調用RenderPage(...)
獲取到API返回來的數據,並根據返回數據計算出頁碼,這樣就能夠綁定數據了。
@if (posts == null) { <Loading /> } else { <div class="post-wrap archive"> @if (posts.Success && posts.Result.Item.Any()) { @foreach (var item in posts.Result.Item) { <h3>@item.Year</h3> @foreach (var post in item.Posts) { <article class="archive-item"> <NavLink href="@("/post" + post.Url)">@post.Title</NavLink> <span class="archive-item-date">@post.CreationTime</span> </article> } } <nav class="pagination"> @for (int i = 1; i <= TotalPage; i++) { var _page = i; if (page == _page) { <span class="page-number current">@_page</span> } else { <a class="page-number" @onclick="@(() => RenderPage(_page))" href="/posts/@_page">@_page</a> } } </nav> } else { <ErrorTip /> } </div> }
在加載數據的時候確定是須要一個等待時間的,由於不可抗拒的緣由數據還沒加載出來的時候,可讓它先轉一會圈圈,當posts
不爲空的時候,再去綁定數據。
在綁定數據,for循環頁碼的時候我又遇到了一個坑😂,這裏不能直接去使用變量i,必須新建一個變量去接受它,否則我傳遞給RenderPage(...)
的參數就會是錯的,始終會取到最後一次循環的i值。
當判斷數據出錯或者沒有數據的時候,在把錯誤提示<ErrorTip />
扔出來顯示。
作到這裏,能夠去運行看看了,確定會報錯,由於還有一個重要的東西沒有改,就是咱們接口的BaseAddress
,在Program.cs
中,默認是當前Blazor項目的運行地址。
咱們須要先將API項目運行起來,拿到地址配置在Program.cs
中,由於如今仍是本地開發,有多種辦法能夠解決,能夠將.HttpApi.Hosting
設爲啓動項目直接運行起來,也可使用命令直接dotnet run
。
我這裏爲了方便,直接發佈在IIS中,後續只要電腦打開就能夠訪問了,你甚至選擇其它任何你能想到的方式。
關於如何發佈這裏先不作展開,有機會的話寫一篇將.net core開發的項目發佈到 Windows、Linux、Docker 的教程吧。
因此個人Program.cs
中配置以下:
//Program.cs using Meowv.Blog.BlazorApp.Commons; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; using System; using System.Net.Http; using System.Threading.Tasks; namespace Meowv.Blog.BlazorApp { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("app"); var baseAddress = "https://localhost"; if (builder.HostEnvironment.IsProduction()) baseAddress = "https://api.meowv.com"; builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(baseAddress) }); builder.Services.AddSingleton(typeof(Common)); await builder.Build().RunAsync(); } } }
baseAddress
默認爲本地開發地址,使用builder.HostEnvironment.IsProduction()
判斷是否爲線上正式生產環境,改變baseAddress
地址。
如今能夠看到已經能夠正常獲取數據,而且翻頁也是OK的,而後又出現了一個新的BUG😂。
細心的能夠發現,當我點擊頭部組件的Posts
a 標籤菜單時候,頁面沒有發生變化,只是路由改變了。
思來想去,我決定使用NavigationManager
這個URI和導航狀態幫助程序來解決,當點擊頭部的Posts
a 標籤菜單直接刷新頁面得了。
在Common.cs
中使用構造函數注入NavigationManager
,而後添加一個跳轉指定URL的方法。
/// <summary> /// 跳轉指定URL /// </summary> /// <param name="uri"></param> /// <param name="forceLoad">true,繞過路由刷新頁面</param> /// <returns></returns> public async Task RenderPage(string url, bool forceLoad = true) { _navigationManager.NavigateTo(url, forceLoad); await Task.CompletedTask; }
當forceLoad = true
的時候,將會繞過路由直接強制刷新頁面,若是forceLoad = false
,則不會刷新頁面。
緊接着在Header.razor
中修改代碼,添加點擊事件。
@*<NavLink class="menu-item" href="posts">Posts</NavLink>*@ <NavLink class="menu-item" href="posts" @onclick="@(async () => await Common.RenderPage("posts"))">Posts</NavLink>
總算是搞定,完成了分頁查詢文章列表的數據綁定,今天就到這裏吧,未完待續...