In this article, I will show you how to filter a paged list (Blazorise DataGrid component) in an ABP Framework application with a Blazor user interface.
The sample application has been developed with Blazor as UI framework and SQL Server as database provider.
The source code of the completed application is available on GitHub.
The following tools are needed to run the solution.
- .NET 5.0 SDK
- vscode, Visual Studio 2019, or another compatible IDE.
- ABP Cli
- Install or update the ABP CLI:
dotnet tool install -g Volo.Abp.Cli || dotnet tool update -g Volo.Abp.Cli
- Use the following ABP CLI command to create a new Blazor ABP application:
abp new AbpFilter -u blazor -o AbpFilter
- Open the solution in Visual Studio (or your favorite IDE).
- Run the
AbpFilter.DbMigrator
application to apply the migrations and seed the initial data. - Run the
AbpFilter.HttpApi.Host
application to start the server-side. - Run the
AbpFilter.Blazor
application to start the Blazor UI project.
To have a simple BookStore application to test with, add the code from the BookStore Tutorial (Part1-2).
Add a BookFilter class to the Books folder of the Domain project
using System;
namespace AbpFilter.Domain.Books
{
public class BookFilter
{
public string Id { get; set; }
public string Name { get; set; }
public string PublishDate { get; set; }
public string Price { get; set; }
}
}
Add an IBookRepository interface to the Books folder of the Domain project.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace AbpFilter.Domain.Books
{
public interface IBookRepository : IRepository<Book, Guid>
{
Task<List<Book>> GetListAsync(int skipCount, int maxResultCount, string sorting = "Name", BookFilter filter = null);
Task<int> GetTotalCountAsync(BookFilter filter);
}
}
Add a BookRepository class to the Books folder of the EntityFrameworkCore project.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AbpFilter.Domain.Books;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using System.Linq.Dynamic.Core;
using Microsoft.EntityFrameworkCore;
namespace AbpFilter.EntityFrameworkCore.Books
{
public class BookRepository : EfCoreRepository<AbpFilterDbContext, Book, Guid>, IBookRepository
{
public BookRepository(IDbContextProvider<AbpFilterDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public async Task<List<Book>> GetListAsync(int skipCount, int maxResultCount, string sorting = "Name", BookFilter filter = null)
{
var dbSet = await GetDbSetAsync();
var books = await dbSet
.WhereIf(!filter.Id.IsNullOrWhiteSpace(), x => x.Id.ToString().Contains(filter.Id))
.WhereIf(!filter.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(filter.Name))
.WhereIf(!filter.Price.IsNullOrWhiteSpace(), x => x.Price.ToString().Contains(filter.Price))
.WhereIf(!filter.PublishDate.IsNullOrWhiteSpace(), x => x.PublishDate.ToString().Contains(filter.PublishDate))
.OrderBy(sorting)
.Skip(skipCount)
.Take(maxResultCount)
.ToListAsync();
return books;
}
public async Task<int> GetTotalCountAsync(BookFilter filter)
{
var dbSet = await GetDbSetAsync();
var books = await dbSet
.WhereIf(!filter.Id.IsNullOrWhiteSpace(), x => x.Id.ToString().Contains(filter.Id))
.WhereIf(!filter.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(filter.Name))
.WhereIf(!filter.Price.IsNullOrWhiteSpace(), x => x.Price.ToString().Contains(filter.Price))
.WhereIf(!filter.PublishDate.IsNullOrWhiteSpace(), x => x.PublishDate.ToString().Contains(filter.PublishDate))
.ToListAsync();
return books.Count;
}
}
}
Add a BookPagedAndSortedResultRequestDto class to the Books folder of the Application.Contracts project. This class inherits the PagedAndSortedResultRequestDto class.
using Volo.Abp.Application.Dtos;
namespace AbpFilter.Application.Contracts.Books
{
public class BookPagedAndSortedResultRequestDto : PagedAndSortedResultRequestDto
{
public string Id { get; set; }
public string Name { get; set; }
public string PublishDate { get; set; }
public string Price { get; set; }
}
}
Update the IBookAppService interface in the Books folder of the Application.Contracts project.
using System;
using Volo.Abp.Application.Services;
namespace AbpFilter.Application.Contracts.Books
{
public interface IBookAppService : ICrudAppService<BookDto, Guid, BookPagedAndSortedResultRequestDto, CreateUpdateBookDto>, IApplicationService
{
}
}
Add a BookAutoMapperProfile class to the Books folder of the Application project.
using AbpFilter.Application.Contracts.Books;
using AbpFilter.Domain.Books;
using AutoMapper;
namespace AbpFilter.Application.Books
{
public class BookAutoMapperProfile : Profile
{
public BookAutoMapperProfile()
{
CreateMap<BookPagedAndSortedResultRequestDto, BookFilter>();
}
}
}
Override the GetListAsync method of the BookAppService class in the Books folder of the Application project, as in the code below:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AbpFilter.Application.Contracts.Books;
using AbpFilter.Domain.Books;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace AbpFilter.Application.Books
{
public class BookAppService : CrudAppService<Book, BookDto, Guid, BookPagedAndSortedResultRequestDto, CreateUpdateBookDto>, IBookAppService
{
private readonly IBookRepository _bookRepository;
public BookAppService(IBookRepository bookRepository) : base(bookRepository)
{
_bookRepository = bookRepository;
}
public override async Task<PagedResultDto<BookDto>> GetListAsync(BookPagedAndSortedResultRequestDto input)
{
var filter = ObjectMapper.Map<BookPagedAndSortedResultRequestDto, BookFilter>(input);
var sorting = (string.IsNullOrEmpty(input.Sorting) ? "Name DESC" : input.Sorting).Replace("ShortName", "Name");
var books = await _bookRepository.GetListAsync(input.SkipCount, input.MaxResultCount, sorting, filter);
var totalCount = await _bookRepository.GetTotalCountAsync(filter);
return new PagedResultDto<BookDto>(totalCount,ObjectMapper.Map<List<Book>, List<BookDto>>(books));
}
}
}
Override the DataGrid component of the Books.razor page in the Blazor project and Make sure you add the Filterable attribute and set it to true
@page "/books"
@using Volo.Abp.BlazoriseUI
@using Volo.Abp.AspNetCore.Components.Web
@using AbpFilter.Application.Contracts.Books
@using AbpFilter.Localization
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<AbpFilterResource> L
@inject AbpBlazorMessageLocalizerHelper<AbpFilterResource> LH
@inherits AbpCrudPageBase<IBookAppService, BookDto, Guid, BookPagedAndSortedResultRequestDto, CreateUpdateBookDto>
<Card>
<CardBody>
<DataGrid TItem="BookDto" Filterable="true" Data="Entities" ReadData="OnDataGridReadAsync" TotalItems="TotalCount" ShowPager="true" PageSize="PageSize">
<DataGridColumns>
<DataGridEntityActionsColumn TItem="BookDto" @ref="@EntityActionsColumn">
<DisplayTemplate>
<EntityActions TItem="BookDto" EntityActionsColumn="@EntityActionsColumn">
<EntityAction TItem="BookDto" Text="@L["Edit"]" Visible="HasUpdatePermission" Clicked="() => OpenEditModalAsync(context)" />
<EntityAction TItem="BookDto" Text="@L["Delete"]" Visible="HasDeletePermission" Clicked="() => DeleteEntityAsync(context)" ConfirmationMessage="() => GetDeleteConfirmationMessage(context)" />
</EntityActions>
</DisplayTemplate>
</DataGridEntityActionsColumn>
<DataGridColumn TItem="BookDto" Field="@nameof(BookDto.Id)" Caption="Id"></DataGridColumn>
<DataGridColumn TItem="BookDto" Field="@nameof(BookDto.Name)" Caption="Name"></DataGridColumn>
<DataGridColumn TItem="BookDto" Field="@nameof(BookDto.PublishDate)" Caption="Publish date"></DataGridColumn>
<DataGridColumn TItem="BookDto" Field="@nameof(BookDto.Price)" Caption="Price"></DataGridColumn>
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>
Override the UpdateGetListInputAsync and OnDataGridReadAsync methods as you can see below:
using System.Linq;
using System.Threading.Tasks;
using AbpFilter.Application.Contracts.Books;
using Blazorise.DataGrid;
using Volo.Abp.Application.Dtos;
namespace AbpFilter.Blazor.Pages
{
public partial class Books
{
protected override Task UpdateGetListInputAsync()
{
if (GetListInput is ISortedResultRequest sortedResultRequestInput)
{
sortedResultRequestInput.Sorting = CurrentSorting;
}
if (GetListInput is IPagedResultRequest pagedResultRequestInput)
{
pagedResultRequestInput.SkipCount = (CurrentPage - 1) * PageSize;
}
if (GetListInput is ILimitedResultRequest limitedResultRequestInput)
{
limitedResultRequestInput.MaxResultCount = PageSize;
}
return Task.CompletedTask;
}
protected override Task OnDataGridReadAsync(DataGridReadDataEventArgs<BookDto> e)
{
var id = e.Columns.FirstOrDefault(c => c.SearchValue != null && c.Field == "Id");
if (id != null) this.GetListInput.Id = id.SearchValue.ToString();
var name = e.Columns.FirstOrDefault(c => c.SearchValue != null && c.Field == "Name");
if (name != null) this.GetListInput.Name = name.SearchValue.ToString();
var publishDate = e.Columns.FirstOrDefault(c => c.SearchValue != null && c.Field == "PublishDate");
if (publishDate != null) this.GetListInput.PublishDate = publishDate.SearchValue.ToString();
var price = e.Columns.FirstOrDefault(c => c.SearchValue != null && c.Field == "Price");
if (price != null) this.GetListInput.Price = price.SearchValue.ToString();
return base.OnDataGridReadAsync(e);
}
}
}
Et voilĂ ! You can now filter a standard paged list (Blazorise DataGrid component) in the ABP Framework.
Get the source code on GitHub.
Enjoy and have fun!