文章详情页面的路由有点点复杂,以/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 "http://www.likecs.com/friendlinks" <Loading />分类、标签、友情链接都是固定的路由,像上面这样就不多说了,然后还剩一个Apps.razor。
//Apps.razor @page "/apps" <div> <div> <h2>- Apps -</h2> <ul> <li> <a target="_blank" href="http://support.qq.com/products/75616"><h3>吐个槽_留言板</h3></a> </li> <li> <NavLink href="http://www.likecs.com/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></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> @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> <NavLink href="http://www.likecs.com/default/index/url/@("/post" + post.Url)">@post.Title</NavLink> <span>@post.CreationTime</span> </article> } } <nav> @for (int i = 1; i <= TotalPage; i++) { var _page = i; if (page == _page) { <span>@_page</span> } else { <a @onclick="@(() => RenderPage(_page))" href="http://www.likecs.com/posts/@_page">@_page</a> } } </nav> } else { <ErrorTip /> } </div> }