series

  1. Develop blog projects based on ABP vNext and.NET Core – build projects using ABP CLI
  2. Develop blog projects based on ABP vNext and.NET Core – slim the project down and make it run
  3. Development blog project based on ABP vNext and.NET Core – Refinement and beautification, Swagger enter
  4. Develop blog project based on ABP vNext and.NET Core – data access and code priority
  5. Development blog project based on ABP vNext and.NET Core – add, delete, change and check custom warehouse
  6. Develop blog project based on ABP vNext and.NET Core – Uniform specification API, wrapper back model
  7. Develop blog projects based on ABP vNext and.NET Core – say Swagger, grouping, description, little green lock
  8. Develop blog projects based on ABP vNext and.NET Core – access GitHub and protect your API with JWT
  9. Develop blog project based on ABP vNext and.NET Core – exception handling and logging
  10. Develop blog projects based on ABP vNext and.NET Core – using Redis to cache data
  11. Develop blog project based on ABP vNext and.NET Core – integrate Hangfire for timed task processing
  12. Develop blog projects based on ABP vNext and.NET Core – Use AutoMapper to map objects
  13. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (Part 1)
  14. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (Part 2)
  15. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (PART 3)
  16. Blog Development project based on ABP vNext and.NET Core
  17. Abp vNext and.NET Core
  18. Blog Development project based on ABP vNext and.NET Core
  19. Blog Development project based on ABP vNext and.NET Core
  20. Blog Development project based on ABP vNext and.NET Core
  21. Abp vNext and.NET Core Development Blog Project – Blazor
  22. Abp vNext and.NET Core Development Blog Project – Blazor – Part 2
  23. Abp vNext and.NET Core Development Blog Project – Blazor
  24. Abp vNext and.NET Core Development Blog Project – Blazor
  25. Abp vNext and.NET Core Development Blog Project – Blazor
  26. Abp vNext and.NET Core Development Blog Project – Blazor – Part 6
  27. Abp vNext and.NET Core Development Blog Project – Blazor
  28. Abp vNext and.NET Core Development Blog Project – Blazor Series (8)
  29. Abp vNext and.NET Core Development Blog Project – Blazor Series (9)
  30. Abp vNext and.NET Core development blog project – Final release project

In the last post, the theme switch of the blog was completed, and the display and hiding functions of menu and TWO-DIMENSIONAL code were completed. In this post, the data display of paging query list of articles was continued.

Add a page

Now when you click a link on the page, you get an error message because the routing address is not found. Create five folders under Pages: Posts, Categories, Tags, Apps, FriendLinks.

Then add the Razor component under the appropriate folder.

  • Posts folder: The Posts list pagePosts.razor, according to the category query article list pagePosts.Category.razorQuery the article list page according to the tagPosts.Tag.razor, article details pagePost.razor
  • Categories folder: Category list pageCategories.razor
  • Tags folder: Tag list pageTags.razor
  • Apps folder:Apps.razorReady to put the link entry inside
  • FriendLinks folder: FriendLinks list pageFriendLinks.razor

Create the Razor components above separately, except for the background CURD page. Now let’s break through them one by one.

The @page directive is used to specify page routing. The official documentation does not support optional parameters, but can support multiple routing rules.

By default, nothing is displayed, and the previous loading loops can be written as a component for each page.

Add the component load.Razor to the Shared folder.

<! --Loading.razor-->
<div class="loader"></div>
Copy the code
//Posts.razor
@page "/posts/"
@page "/posts/page/{page:int}"
@page "/posts/{page:int}"

<Loading />

@code {
    /// <summary>
    ///The current page number
    /// </summary>
    [Parameter]
    public int? page { get; set; }}Copy the code

/posts/page/{page:int} /posts/page/{page:int} /posts/page/{page:int} /posts/1, /posts/page/1.

//Posts.Category.razor
@page "/category/{name}"

<Loading />

@code {
    /// <summary>
    ///Category name parameter
    /// </summary>
    [Parameter]
    public string name { get; set; }}Copy the code

Query the article list page by category name. Name is used as the category name parameter to match routes like /category/aaa and /category/ BBB.

//Posts.Tag.razor
@page "/tag/{name}"

<Loading />

@code {
    /// <summary>
    ///Label Name Parameter
    /// </summary>
    [Parameter]
    public string name { get; set; }}Copy the code

The query article list by tag name page looks much the same, matching routes like /tag/aaa and /tag/ BBB.

//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; }}Copy the code

The route to the article details page is a bit complicated, starting with /post/, plus the year, month and day, and the semantic name of the current article. /post/2020/06/09/aaa/POST /2020/06/9/ BBB /aaa /post/2020/06/9/ BBB /aaa /post/2020/06/9/ BBB

//Categories.razor
@page "/categories"

<Loading />

//Tags.razor
@page "/tags"

<Loading />

//FriendLinks.razor
@page "/friendlinks"

<Loading />
Copy the code

Categories, tags, and friendship links are all fixed routes, so I won’t talk about them too much, and then there is an app.Razor.

//Apps.razor
@page "/apps"

<div class="container">
    <div class="post-wrap">
        <h2 class="post-title">-&nbsp; Apps&nbsp; -</h2>
        <ul>
            <li>
                <a target="_blank" href="https://support.qq.com/products/75616"><h3>Comment _ message board</h3></a>
            </li>
            <li>
                <NavLink href="/friendlinks"><h3>link</h3></NavLink>
            </li>
        </ul>
    </div>
</div>
Copy the code

Inside added a friendship link entrance, and a Tencent rabbit small nest link, welcome to ridicule message oh.

Now you can run it and see that clicking all links will not prompt an error, as long as the route matches correctly will appear in the loading circle.

The article lists

In Blazor, I tried to dynamically accept the json data that was passed to me, and it didn’t work at all. So when I’m asking the API to receive the data, I need to specify the object to receive it, and that’s pretty easy and I’ll just refer to the API. Application.Contracts will do, but then the pits come and the target frames will not match and will not run after referencing.

The compromise is to manually copy the Dtos available for the returned objects in the API into the Blazor project, and then consider using the public return model as a Nuget package.

The final result is to add a Response folder in Blazor to hold the receive object.

A little silly, first solve this, and then do further optimization.

Add a reference to the item we copied in _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
Copy the code

Inject HttpClient Http: Inject HttpClient to request API data.

Now that you have the receiving object, it’s easy to implement paging query for the list of articles.

Add three private variables to limit the number of articles loaded at a time, the total page number used to calculate pages, and the API’s receive type parameters for return data.

/// <summary>
///Limit the number of
/// </summary>
private int Limit = 15;

/// <summary>
///Total page number
/// </summary>
private int TotalPage;

/// <summary>
///Article list data
/// </summary>
private ServiceResult<PagedList<QueryPostDto>> posts;
Copy the code

Then, when the page is initialized, load the data and render the page. Take into account that the page parameter may be empty and give it a default value of 1 when empty.

/// <summary>
///Initialize the
/// </summary>
protected override async Task OnInitializedAsync()
{
    // Set the default value
    page = page.HasValue ? page : 1;

    await RenderPage(page);
}

/// <summary>
///Click on the page number to re-render the data
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
private async Task RenderPage(int? page)
{
    // Get data
    posts = await Http.GetFromJsonAsync<ServiceResult<PagedList<QueryPostDto>>>($"/blog/posts? page={page}&limit={Limit}");

    // Count the total page number
    TotalPage = (int)Math.Ceiling((posts.Result.Total / (double)Limit));
}
Copy the code

Set the default values in the initialization method, calling RenderPage(…) Get the data returned from the API and calculate the page number based on the returned data, so you can bind the data.

@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>
}
Copy the code

There is definitely a wait time when the data is loading. If the data is not loading for some irresistible reason, you can let it go around for a while and then bind the data when the posts are not empty.

I can’t use variable I directly, I have to create a new variable to accept it, otherwise I pass it to RenderPage(…). Will be wrong and will always take the I value of the last loop.

Error
is thrown out to display when judging data error or no data.

There is one important thing that has not been changed: the BaseAddress of our interface. In program.cs, the default is the current Blazor project’s run address.

We need to run the API project first, get the address to configure in program.cs, because now it is still local development, there are many ways to solve, you can put. HttpApi.Hosting is set to start the project and run directly, or you can use the dotnet run command directly.

I’m just going to publish it in IIS for convenience, and then you can access it as soon as your computer is turned on, and you can do it any other way you can think of.

How to release.net Core projects to Windows, Linux, and Docker?

So my program. cs configuration is as follows:

//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));

            awaitbuilder.Build().RunAsync(); }}}Copy the code

BaseAddress default address for local development, using builder. HostEnvironment. IsProduction () to determine whether to online formal production environment, change the baseAddress address.

Now you can see that the data can be retrieved normally, and the page-turning is OK, then there is a new BUG😂.

To solve the BUG

When I click on the Postsa TAB menu of the header component, the page does not change, only the route changes.

After much thought, I decided to use NavigationManager, a URI and navigational state helper, to solve the problem when clicking on the Postsa TAB menu in the header to refresh the page directly.

Inject NavigationManager with the constructor in common.cs, and then add a method to jump to the specified URL.

/// <summary>
///Jump to a specified URL
/// </summary>
/// <param name="uri"></param>
/// <param name="forceLoad">True, bypassing the route to refresh the page</param>
/// <returns></returns>
public async Task RenderPage(string url, bool forceLoad = true)
{
    _navigationManager.NavigateTo(url, forceLoad);

    await Task.CompletedTask;
}
Copy the code

When forceLoad = true, the route is bypassed and the page is forced to refresh. If forceLoad = false, the page is not refreshed.

Then modify the code in header.Razor to add the click event.

@ *<NavLink class="menu-item" href="posts">Posts</NavLink>* @<NavLink class="menu-item" href="posts" @onclick="@(async () => await Common.RenderPage("posts"))">Posts</NavLink>
Copy the code

Finally, we have completed the data binding of the paginated query article list.

Open source: github.com/Meowv/Blog/…