MudBlazor Theme in ABP Blazor WebAssembly FINAL

Introduction

In this part, I will show you how to customize/override pre-built Blazor pages of the Identity Module using MudBlazor components. I assume that Tenant and Setting management pages are rarely used by end-users, so these modules are not covered in this sample to keep it short. You can customize every page using the principles shown here. All you need to do is:

  1. Deriving a class that inherits the class you want to override
  2. And adding the following attributes to the derived class:
@* to the razor file *@
@attribute [ExposeServices(typeof(BASE-CLASS))]
@attribute [Dependency(ReplaceServices = true)]
// or to the C# file
[ExposeServices(typeof(BASE-CLASS))]
[Dependency(ReplaceServices = true)]

See Blazor UI: Customization / Overriding Components for more information.

16. Make Sure You Have Completed Previous Parts

You must complete the steps in Part 1, Part 2, Part 3, and Part 4 to continue.

17. Create Required Files

Create Identity folder under Acme.BookStore.Blazor/Pages/.

Create the following files in this folder:

  1. MudPermissionManagementModal.razor
  2. MudPermissionManagementModal.razor.cs
  3. MudRoleManagement.razor
  4. MudRoleManagement.razor.cs
  5. MudUserManagement.razor
  6. MudUserManagement.razor.cs

18. Modify These Files' Content As Shown Below

  1. MudPermissionManagementModal.razor
@using Microsoft.Extensions.Localization
@using Volo.Abp.PermissionManagement.Blazor.Components
@using Volo.Abp.PermissionManagement.Localization
@inherits PermissionManagementModal

<MudDialog @bind-IsVisible="_isVisible"
           Options="DialogOptions">
    <TitleContent>
        <MudText Typo="Typo.h6">                    
            @L["Permissions"] - @_entityDisplayName
        </MudText>
    </TitleContent>
    <DialogContent>
        <MudContainer>
            <MudCheckBox T="bool" @bind-Checked="@GrantAll">
                @L["SelectAllInAllTabs"]
            </MudCheckBox>
            <MudDivider />
            @if (_groups != null)
            {
                <MudTabs Position="MudBlazor.Position.Left" >
                    <TabPanelHeader>
                        <MudText>
                            @* ?! *@
                            &nbsp;&nbsp;&nbsp;&nbsp;
                        </MudText>
                    </TabPanelHeader>
                    <ChildContent>
                        @foreach (var group in _groups)
                        {
                            <MudTabPanel BadgeColor="MudBlazor.Color.Secondary"
                                         BadgeData="@(group.Permissions.Count(x => x.IsGranted))"
                                         Text="@(group.DisplayName)">
                                <MudContainer Style="width: 400px; height: 400px; overflow-y: scroll">
                                    <MudGrid>
                                        <MudItem xs="12">
                                            <MudText Typo="Typo.h6">@group.DisplayName</MudText>
                                            <MudDivider />
                                        </MudItem>
                                        <MudItem xs="12">
                                            <MudCheckBox T="bool"
                                                        Checked="@(group.Permissions.All(x => x.IsGranted))"
                                                        CheckedChanged="@(b => GroupGrantAllChanged(b, group))">
                                            @L["SelectAllInThisTab"]
                                        </MudCheckBox>
                                        <MudDivider />
                                        </MudItem>
                                        @foreach (var permission in group.Permissions)
                                        {
                                            <MudItem xs="1">
                                            </MudItem>
                                            <MudItem xs="11">
                                                <MudCheckBox T="bool"
                                                                Style="padding-inline-start: 16px;"
                                                                Disabled="@(IsDisabledPermission(permission))"
                                                                Checked="@permission.IsGranted"
                                                                CheckedChanged="@(b => PermissionChanged(b, group, permission))">
                                                    @GetShownName(permission)
                                                </MudCheckBox>
                                            </MudItem>
                                        }
                                    </MudGrid>
                                </MudContainer>
                            </MudTabPanel>
                        }
                    </ChildContent>
                </MudTabs>
            }
        </MudContainer>
    </DialogContent>
    <DialogActions>
        <MudButton Color="MudBlazor.Color.Secondary" 
                   OnClick="CloseModalAsync">
            @L["Cancel"]
        </MudButton>
        <MudButton Variant="Variant.Filled" 
                   Color="MudBlazor.Color.Primary" 
                   OnClick="SaveAsync">
            @L["Save"]
        </MudButton>
    </DialogActions>
</MudDialog>

  1. MudPermissionManagementModal.razor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using Volo.Abp.AspNetCore.Components.Web.Configuration;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.Localization;

namespace Acme.BookStore.Blazor.Pages.Identity;

public partial class MudPermissionManagementModal
{
    [Inject] private IPermissionAppService PermissionAppService { get; set; }
    [Inject] private ICurrentApplicationConfigurationCacheResetService CurrentApplicationConfigurationCacheResetService { get; set; }

    private bool _isVisible;

    protected virtual DialogOptions DialogOptions
    {
        get => new()
        {
            CloseButton = true,
            CloseOnEscapeKey = true,
            DisableBackdropClick = true,
            MaxWidth = MaxWidth.Medium
        };
    }

    private string _providerName;
    private string _providerKey;

    private string _entityDisplayName;
    private List<PermissionGroupDto> _groups;

    private readonly List<PermissionGrantInfoDto> _disabledPermissions = new();

    private int _grantedPermissionCount = 0;
    private int _notGrantedPermissionCount = 0;

    private bool GrantAll
    {
        get => _notGrantedPermissionCount == 0;
        set
        {
            if (_groups == null) return;

            _grantedPermissionCount = 0;
            _notGrantedPermissionCount = 0;

            foreach (var permission in _groups.SelectMany(x => x.Permissions))
            {
                if (IsDisabledPermission(permission)) continue;

                permission.IsGranted = value;

                if (value)
                    _grantedPermissionCount++;
                else
                    _notGrantedPermissionCount++;
            }
        }
    }

    public MudPermissionManagementModal()
    {
        LocalizationResource = typeof(AbpPermissionManagementResource);
    }

    public async Task OpenDialogAsync(string providerName, string providerKey, string entityDisplayName = null)
    {
        try
        {
            _providerName = providerName;
            _providerKey = providerKey;

            var result = await PermissionAppService.GetAsync(_providerName, _providerKey);

            _entityDisplayName = entityDisplayName ?? result.EntityDisplayName;
            _groups = result.Groups;

            _grantedPermissionCount = 0;
            _notGrantedPermissionCount = 0;
            foreach (var permission in _groups.SelectMany(x => x.Permissions))
            {
                if (permission.IsGranted && permission.GrantedProviders.All(x => x.ProviderName != _providerName))
                {
                    _disabledPermissions.Add(permission);
                    continue;
                }

                if (permission.IsGranted)
                    _grantedPermissionCount++;
                else
                    _notGrantedPermissionCount++;
            }

            _isVisible = true;
            StateHasChanged();
        }
        catch (Exception e)
        {
            await HandleErrorAsync(e);
        }
    }

    private async Task CloseModalAsync()
    {
        await InvokeAsync(() => { _isVisible = false; });
    }

    private async Task SaveAsync()
    {
        try
        {
            var updateDto = new UpdatePermissionsDto
            {
                Permissions = _groups
                    .SelectMany(g => g.Permissions)
                    .Select(p => new UpdatePermissionDto { IsGranted = p.IsGranted, Name = p.Name })
                    .ToArray()
            };

            await PermissionAppService.UpdateAsync(_providerName, _providerKey, updateDto);

            await CurrentApplicationConfigurationCacheResetService.ResetAsync();

            await CloseModalAsync();
        }
        catch (Exception e)
        {
            await HandleErrorAsync(e);
        }
    }

    private void GroupGrantAllChanged(bool value, PermissionGroupDto permissionGroup)
    {
        foreach (var permission in permissionGroup.Permissions)
        {
            if (!IsDisabledPermission(permission))
                SetPermissionGrant(permission, value);
        }
    }

    private void PermissionChanged(bool value, PermissionGroupDto permissionGroup, PermissionGrantInfoDto permission)
    {
        SetPermissionGrant(permission, value);

        if (value && permission.ParentName != null)
        {
            var parentPermission = GetParentPermission(permissionGroup, permission);

            SetPermissionGrant(parentPermission, true);
        }
        else if (value == false)
        {
            var childPermissions = GetChildPermissions(permissionGroup, permission);

            foreach (var childPermission in childPermissions)
            {
                SetPermissionGrant(childPermission, false);
            }
        }
    }

    private void SetPermissionGrant(PermissionGrantInfoDto permission, bool value)
    {
        if (permission.IsGranted == value) return;

        if (value)
        {
            _grantedPermissionCount++;
            _notGrantedPermissionCount--;
        }
        else
        {
            _grantedPermissionCount--;
            _notGrantedPermissionCount++;
        }

        permission.IsGranted = value;
    }

    private static PermissionGrantInfoDto GetParentPermission(PermissionGroupDto permissionGroup, PermissionGrantInfoDto permission)
    {
        return permissionGroup.Permissions.First(x => x.Name == permission.ParentName);
    }

    private static List<PermissionGrantInfoDto> GetChildPermissions(PermissionGroupDto permissionGroup, PermissionGrantInfoDto permission)
    {
        return permissionGroup.Permissions.Where(x => x.Name.StartsWith(permission.Name)).ToList();
    }

    private bool IsDisabledPermission(PermissionGrantInfoDto permissionGrantInfo)
    {
        return _disabledPermissions.Any(x => x == permissionGrantInfo);
    }

    private string GetShownName(PermissionGrantInfoDto permissionGrantInfo)
    {
        if (!IsDisabledPermission(permissionGrantInfo))
            return permissionGrantInfo.DisplayName;

        return string.Format(
            "{0} ({1})",
            permissionGrantInfo.DisplayName,
            permissionGrantInfo.GrantedProviders
                .Where(p => p.ProviderName != _providerName)
                .Select(p => p.ProviderName)
                .JoinAsString(", ")
        );
    }
}

  1. MudRoleManagement.razor
@using MudBlazor;
@using Blazorise
@using Blazorise.DataGrid
@using Volo.Abp.BlazoriseUI
@using Volo.Abp.BlazoriseUI.Components
@using Volo.Abp.Identity
@using Microsoft.AspNetCore.Authorization
@using Volo.Abp.PermissionManagement.Blazor.Components
@using Volo.Abp.Identity.Localization
@using Volo.Abp.AspNetCore.Components.Web
@using Volo.Abp.AspNetCore.Components.Web.Theming
@using Volo.Abp.BlazoriseUI.Components.ObjectExtending
@using Volo.Abp.AspNetCore.Components.Web.Theming.Layout
@using Volo.Abp.DependencyInjection
@using Volo.Abp.Identity.Blazor.Pages.Identity
@attribute [Authorize(IdentityPermissions.Roles.Default)]
@attribute [ExposeServices(typeof(RoleManagement))]
@attribute [Dependency(ReplaceServices = true)]
@inject AbpBlazorMessageLocalizerHelper<IdentityResource> LH

@inherits RoleManagement

@* ************************* DATA GRID ************************* *@
<MudDataGrid T="IdentityRoleDto"
             @ref="@_dataGrid"
             Elevation="8"
             Hideable="true"
             Striped="true"
             ServerData="LoadServerData">
    <ToolBarContent>
        <MudText Typo="Typo.h5">@L["Roles"]</MudText>
        <MudSpacer />
        <MudButton Color="MudBlazor.Color.Primary"
                   Variant="Variant.Outlined"
                   Disabled="!HasCreatePermission"
                   OnClick="OpenCreateModalAsync">
            @L["NewRole"]
        </MudButton>
    </ToolBarContent>
    <Columns>
        <MudBlazor.Column T="IdentityRoleDto"
                          Field="@nameof(IdentityRoleDto.Id)"
                          Hideable="false"
                          Title="@L["Actions"]">
            <CellTemplate>
                @if (HasUpdatePermission)
                {
                    <MudIconButton Icon="fas fa-edit" 
                                   OnClick="@(async (_) => { await OpenEditModalAsync(context.Item); })"
                                   Size="MudBlazor.Size.Small" />
                    <MudIconButton Icon="fas fa-user-lock" 
                                   OnClick="@(async (_) => { await OpenPermissionsModalAsync(context.Item); })"
                                   Size="MudBlazor.Size.Small" />
                }
                @if (HasDeletePermission)
                {   
                    <MudIconButton Icon="fas fa-trash" 
                                   OnClick="@(async (_) => { await DeleteEntityAsync(context.Item);} )"
                                   Size="MudBlazor.Size.Small" />
                }
            </CellTemplate>
        </MudBlazor.Column>
        <MudBlazor.Column T="IdentityRoleDto"
                          Field="@nameof(IdentityRoleDto.Name)"
                          Hideable="false"
                          Title=@L["Name"] />
        <MudBlazor.Column T="IdentityRoleDto"
                          Field="@nameof(IdentityRoleDto.Name)"
                          Title="">
            <CellTemplate>
                @if (context.Item.IsDefault)
                {
                    <MudChip Color="MudBlazor.Color.Success">
                        @L["DisplayName:IsDefault"]
                    </MudChip>
                }
                @if (context.Item.IsPublic)
                {
                    <MudChip Color="MudBlazor.Color.Info">
                        @L["DisplayName:IsPublic"]
                    </MudChip>
                }
            </CellTemplate>
        </MudBlazor.Column>
    </Columns>
</MudDataGrid>

@* ************************* CREATE MODAL ************************* *@
@if (HasCreatePermission)
{
    <MudDialog @bind-IsVisible="_createDialogVisible"
               Options="DialogOptions">
        <TitleContent>
            <MudText Typo="Typo.h6">@L["NewRole"]</MudText>
        </TitleContent>
        <DialogContent>
            <MudForm Model="@NewEntity"
                     @ref="_createForm">
                <MudGrid>
                    <MudItem xs="12">
                        <MudTextField @bind-Value="@NewEntity.Name" 
                                      Label=@L["Name"]
                                      For=@(() => NewEntity.Name) />
                    </MudItem>
                    <MudItem xs="6">
                        <MudCheckBox T="bool" @bind-Checked="@NewEntity.IsDefault">
                            @L["DisplayName:IsDefault"]
                        </MudCheckBox>
                    </MudItem>
                    <MudItem xs="6">
                        <MudCheckBox T="bool" @bind-Checked="@NewEntity.IsPublic">
                            @L["DisplayName:IsPublic"]
                        </MudCheckBox>
                    </MudItem>
                </MudGrid>
            </MudForm>
        </DialogContent>
        <DialogActions>
            <MudButton Color="MudBlazor.Color.Secondary" 
                       OnClick="CloseCreateModalAsync">
                @L["Cancel"]
            </MudButton>
            <MudButton Variant="Variant.Filled" 
                       Color="MudBlazor.Color.Primary" 
                       OnClick="CreateEntityAsync">
                @L["Save"]
            </MudButton>
        </DialogActions>
    </MudDialog>
}

@* ************************* EDIT MODAL ************************* *@
@if (HasUpdatePermission)
{
    <MudDialog @bind-IsVisible="_editDialogVisible"
               Options="DialogOptions">
        <TitleContent>
            <MudText Typo="Typo.h6">@L["Edit"]</MudText>
        </TitleContent>
        <DialogContent>
            <MudForm Model="@EditingEntity"
                     @ref="_editForm">
                <MudGrid>
                    <MudItem xs="12">
                        <MudTextField @bind-Value="@EditingEntity.Name" 
                                      Label=@L["Name"]
                                      For=@(() => EditingEntity.Name) />
                    </MudItem>
                    <MudItem xs="6">
                        <MudCheckBox T="bool" @bind-Checked="@EditingEntity.IsDefault">
                            @L["DisplayName:IsDefault"]
                        </MudCheckBox>
                    </MudItem>
                    <MudItem xs="6">
                        <MudCheckBox T="bool" @bind-Checked="@EditingEntity.IsPublic">
                            @L["DisplayName:IsPublic"]
                        </MudCheckBox>
                    </MudItem>
                </MudGrid>
            </MudForm>
        </DialogContent>
        <DialogActions>
            <MudButton Color="MudBlazor.Color.Secondary" 
                       OnClick="CloseEditModalAsync">
                @L["Cancel"]
            </MudButton>
            <MudButton Variant="Variant.Filled" 
                       Color="MudBlazor.Color.Primary" 
                       OnClick="UpdateEntityAsync">
                @L["Save"]
            </MudButton>
        </DialogActions>
    </MudDialog>
}

@if (HasManagePermissionsPermission)
{
    <MudPermissionManagementModal @ref="_permissionManagementModal"/>
}

  1. MudRoleManagement.razor.cs
using System.Threading.Tasks;
using MudBlazor;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Identity;

namespace Acme.BookStore.Blazor.Pages.Identity;

public partial class MudRoleManagement
{
    private MudForm _createForm;
    private MudForm _editForm;
    private bool _createDialogVisible;
    private bool _editDialogVisible;
    private MudDataGrid<IdentityRoleDto> _dataGrid;
    private MudPermissionManagementModal _permissionManagementModal;

    protected async Task<GridData<IdentityRoleDto>> LoadServerData(GridState<IdentityRoleDto> state)
    {
        if (GetListInput is PagedAndSortedResultRequestDto pageResultRequest)
        {
            pageResultRequest.MaxResultCount = state.PageSize;
            pageResultRequest.SkipCount = state.Page * state.PageSize;
        }
        var result = await AppService.GetListAsync(GetListInput);
        return new()
        {
            Items = result.Items,
            TotalItems = (int)result.TotalCount
        };
    }

    protected override Task OpenCreateModalAsync()
    {
        NewEntity = new();
        _createDialogVisible = true;
        return Task.CompletedTask;
    }

    protected override Task CloseCreateModalAsync()
    {
        _createDialogVisible = false;
        return Task.CompletedTask;
    }

    protected override async Task CreateEntityAsync()
    {
        if (_createForm.IsValid)
        {
            var createDto = MapToCreateInput(NewEntity);
            await AppService.CreateAsync(createDto);
            await _dataGrid.ReloadServerData();
            _createDialogVisible = false;
        }
    }

    protected override Task OpenEditModalAsync(IdentityRoleDto entity)
    {
        EditingEntityId = entity.Id;
        EditingEntity = MapToEditingEntity(entity);
        _editDialogVisible = true;
        return Task.CompletedTask;
    }

    protected override Task CloseEditModalAsync()
    {
        _editDialogVisible = false;
        return Task.CompletedTask;
    }

    protected override async Task UpdateEntityAsync()
    {
        if (_editForm.IsValid)
        {
            await base.UpdateEntityAsync();
            await _dataGrid.ReloadServerData();
            _editDialogVisible = false;
        }
    }

    protected override async Task DeleteEntityAsync(IdentityRoleDto entity)
    {
        if (await Message.Confirm(GetDeleteConfirmationMessage(entity)))
        {
            await base.DeleteEntityAsync(entity);
            await _dataGrid.ReloadServerData();
        }
    }

    protected override Task OnUpdatedEntityAsync()
    {
        return Task.CompletedTask;
    }

    protected virtual async Task OpenPermissionsModalAsync(IdentityRoleDto entity)
    {
        await _permissionManagementModal.OpenDialogAsync(PermissionProviderName, entity.Name);
    }

    protected virtual DialogOptions DialogOptions
    {
        get => new()
        {
            CloseButton = true,
            CloseOnEscapeKey = true,
            DisableBackdropClick = true
        };
    }
}

  1. MudUserManagement.razor
@using Microsoft.AspNetCore.Authorization
@using Volo.Abp.DependencyInjection
@using Volo.Abp.PermissionManagement.Blazor.Components
@using Volo.Abp.Identity.Localization
@using Volo.Abp.AspNetCore.Components.Web.Theming.Layout
@using Volo.Abp.Identity
@using Volo.Abp.Identity.Blazor.Pages.Identity
@attribute [Authorize(IdentityPermissions.Users.Default)]
@attribute [ExposeServices(typeof(UserManagement))]
@attribute [Dependency(ReplaceServices = true)]
@inject AbpBlazorMessageLocalizerHelper<IdentityResource> LH

@inherits UserManagement

@* ************************* DATA GRID ************************* *@
<MudDataGrid T="IdentityUserDto"
             @ref="@_dataGrid"
             Striped="true"
             Elevation="8"
             Hideable="true"
             ServerData="LoadServerData">
    <ToolBarContent>
        <MudText Typo="Typo.h5">@L["Users"]</MudText>
        <MudSpacer />
        <MudButton Color="MudBlazor.Color.Primary"
                   Variant="Variant.Outlined"
                   Disabled="!HasCreatePermission"
                   OnClick="OpenCreateModalAsync">
            @L["NewUser"]
        </MudButton>
    </ToolBarContent>
    <Columns>
        <MudBlazor.Column T="IdentityUserDto"
                          Field="@nameof(IdentityUserDto.Id)"
                          Hideable="false"
                          Title=@L["Actions"]>
            <CellTemplate>
                @if (HasUpdatePermission)
                {
                    <MudIconButton Icon="fas fa-edit"
                                   OnClick="@(async (_) => { await OpenEditModalAsync(context.Item); })"
                                   Size="MudBlazor.Size.Small" />
                    <MudIconButton Icon="fas fa-user-lock"
                                   OnClick="@(async (_) => { await OpenPermissionsModalAsync(context.Item); })"
                                   Size="MudBlazor.Size.Small" />
                }
                @if (HasDeletePermission)
                {
                    <MudIconButton Icon="fas fa-trash"
                                   OnClick="@(async (_) => { await DeleteEntityAsync(context.Item);} )"
                                   Size="MudBlazor.Size.Small" />
                }
            </CellTemplate>
        </MudBlazor.Column>
        <MudBlazor.Column T="IdentityUserDto"
                          Field="@nameof(IdentityUserDto.Name)"
                          Hideable="false"
                          Title=@L["Name"] />
        <MudBlazor.Column T="IdentityUserDto"
                          Field="@nameof(IdentityUserDto.Email)"
                          Title=@L["Email"] />
        <MudBlazor.Column T="IdentityUserDto"
                          Field="@nameof(IdentityUserDto.PhoneNumber)"
                          Title=@L["PhoneNumber"] />
    </Columns>
</MudDataGrid>

@* ************************* CREATE MODAL ************************* *@
@if (HasCreatePermission)
{
    <MudDialog @bind-IsVisible="_createDialogVisible"
               Options="DialogOptions">
        <TitleContent>
            <MudText Typo="Typo.h6">@L["NewUser"]</MudText>
        </TitleContent>
        <DialogContent>
            <MudForm Model="@NewEntity"
                     @ref="@_createForm">
                <MudContainer>
                    <MudTabs>
                        <MudTabPanel Text=@L["UserInformations"]>
                            <MudContainer Style="width: 450px; height: 450px; overflow-y: scroll">
                                <MudGrid>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@NewEntity.UserName"
                                                      Label=@L["DisplayName:UserName"]
                                                      For="@(() => NewEntity.UserName)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@NewEntity.Name"
                                                      Label=@L["DisplayName:Name"]
                                                      For="@(() => NewEntity.Name)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@NewEntity.Surname"
                                                      Label=@L["DisplayName:Surname"]
                                                      For="@(() => NewEntity.Surname)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@NewEntity.Password"
                                                      Label=@L["DisplayName:Password"]
                                                      InputType="InputType.Password"
                                                      For="@(() => NewEntity.Password)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@NewEntity.Email"
                                                      Label=@L["DisplayName:Email"]
                                                      InputType="InputType.Email"
                                                      For="@(() => NewEntity.Email)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@NewEntity.PhoneNumber"
                                                      Label=@L["DisplayName:PhoneNumber"]
                                                      InputType="InputType.Telephone"
                                                      For="@(() => NewEntity.PhoneNumber)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudCheckBox TValue="bool" 
                                                     @bind-Checked="@NewEntity.IsActive">
                                            @L["DisplayName:IsActive"]
                                        </MudCheckBox>
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudCheckBox TValue="bool" 
                                                     @bind-Checked="@NewEntity.LockoutEnabled">
                                            @L["DisplayName:LockoutEnabled"]
                                        </MudCheckBox>
                                    </MudItem>
                                </MudGrid>
                            </MudContainer>
                        </MudTabPanel>
                        <MudTabPanel Text=@L["Roles"]>
                            <MudContainer Style="width: 450px; height: 450px; overflow-y: scroll">
                                <MudGrid>
                                    @if (NewUserRoles != null)
                                    {
                                        @foreach (var role in NewUserRoles)
                                        {
                                            <MudItem xs="12">
                                                <MudCheckBox TValue="bool" 
                                                             @bind-Checked="@role.IsAssigned">
                                                    @role.Name
                                                </MudCheckBox>
                                            </MudItem>
                                        }
                                    }
                                    else
                                    {
                                        <MudItem xs="12">
                                            <MudText>N/A</MudText>
                                        </MudItem>
                                    }
                                </MudGrid>
                            </MudContainer>
                        </MudTabPanel>
                    </MudTabs>
                </MudContainer>
            </MudForm>
        </DialogContent>
        <DialogActions>
            <MudButton Color="MudBlazor.Color.Secondary" 
                       OnClick="CloseCreateModalAsync">
                @L["Cancel"]
            </MudButton>
            <MudButton Variant="Variant.Filled" 
                       Color="MudBlazor.Color.Primary" 
                       OnClick="CreateEntityAsync">
                @L["Save"]
            </MudButton>
        </DialogActions>
    </MudDialog>
}

@* ************************* EDIT MODAL ************************* *@
@if (HasUpdatePermission)
{
    <MudDialog @bind-IsVisible="_editDialogVisible"
               Options="DialogOptions">
        <TitleContent>
            <MudText Typo="Typo.h6">@L["Edit"]</MudText>
        </TitleContent>
        <DialogContent>
            <MudForm Model="@EditingEntity"
                     @ref="@_editForm">
                <MudContainer>
                    <MudTabs>
                        <MudTabPanel Text=@L["UserInformations"]>
                            <MudContainer Style="width: 450px; height: 450px; overflow-y: scroll">
                                <MudGrid>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@EditingEntity.UserName"
                                                      Label=@L["DisplayName:UserName"]
                                                      For="@(() => EditingEntity.UserName)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@EditingEntity.Name"
                                                      Label=@L["DisplayName:Name"]
                                                      For="@(() => EditingEntity.Name)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@EditingEntity.Surname"
                                                      Label=@L["DisplayName:Surname"]
                                                      For="@(() => EditingEntity.Surname)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@EditingEntity.Password"
                                                      Label=@L["DisplayName:Password"]
                                                      InputType="InputType.Password"
                                                      For="@(() => EditingEntity.Password)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@EditingEntity.Email"
                                                      Label=@L["DisplayName:Email"]
                                                      InputType="InputType.Email"
                                                      For="@(() => EditingEntity.Email)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudTextField @bind-Value="@EditingEntity.PhoneNumber"
                                                      Label=@L["DisplayName:PhoneNumber"]
                                                      InputType="InputType.Telephone"
                                                      For="@(() => EditingEntity.PhoneNumber)" />
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudCheckBox TValue="bool" 
                                                     @bind-Checked="@EditingEntity.IsActive">
                                            @L["DisplayName:IsActive"]
                                        </MudCheckBox>
                                    </MudItem>
                                    <MudItem xs="12">
                                        <MudCheckBox TValue="bool" 
                                                     @bind-Checked="@EditingEntity.LockoutEnabled">
                                            @L["DisplayName:LockoutEnabled"]
                                        </MudCheckBox>
                                    </MudItem>
                                </MudGrid>
                            </MudContainer>
                        </MudTabPanel>
                        <MudTabPanel Text=@L["Roles"]>
                            <MudContainer Style="width: 450px; height: 450px; overflow-y: scroll">
                                <MudGrid>
                                    @if (EditUserRoles != null)
                                    {
                                        @foreach (var role in EditUserRoles)
                                        {
                                            <MudItem xs="12">
                                                <MudCheckBox TValue="bool" 
                                                             @bind-Checked="@role.IsAssigned">
                                                    @role.Name
                                                </MudCheckBox>
                                            </MudItem>
                                        }
                                    }
                                    else
                                    {
                                        <MudItem xs="12">
                                            <MudText>N/A</MudText>
                                        </MudItem>
                                    }
                                </MudGrid>
                            </MudContainer>
                        </MudTabPanel>
                    </MudTabs>
                </MudContainer>
            </MudForm>
        </DialogContent>
        <DialogActions>
            <MudButton Color="MudBlazor.Color.Secondary" 
                       OnClick="CloseEditModalAsync">
                @L["Cancel"]
            </MudButton>
            <MudButton Variant="Variant.Filled" 
                       Color="MudBlazor.Color.Primary" 
                       OnClick="UpdateEntityAsync">
                @L["Save"]
            </MudButton>
        </DialogActions>
    </MudDialog>
}

@if (HasManagePermissionsPermission)
{
    <MudPermissionManagementModal @ref="_permissionManagementModal" />
}

  1. MudUserManagement.razor.cs
using System.Linq;
using System.Threading.Tasks;
using MudBlazor;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Blazor.Pages.Identity;

namespace Acme.BookStore.Blazor.Pages.Identity;

public partial class MudUserManagement
{
    private MudForm _createForm;
    private MudForm _editForm;
    private bool _createDialogVisible;
    private bool _editDialogVisible;
    private MudDataGrid<IdentityUserDto> _dataGrid;
    private MudPermissionManagementModal _permissionManagementModal;

    protected async Task<GridData<IdentityUserDto>> LoadServerData(GridState<IdentityUserDto> state)
    {
        if (GetListInput is PagedAndSortedResultRequestDto pageResultRequest)
        {
            pageResultRequest.MaxResultCount = state.PageSize;
            pageResultRequest.SkipCount = state.Page * state.PageSize;
        }
        var result = await AppService.GetListAsync(GetListInput);
        return new()
        {
            Items = result.Items,
            TotalItems = (int)result.TotalCount
        };
    }

    protected override Task OpenCreateModalAsync()
    {
        NewEntity = new() { IsActive = true };
        NewUserRoles = Roles.Select(i => new AssignedRoleViewModel
        {
            Name = i.Name,
            IsAssigned = i.IsDefault
        }).ToArray();
        _createDialogVisible = true;
        return Task.CompletedTask;
    }

    protected override Task CloseCreateModalAsync()
    {
        _createDialogVisible = false;
        return Task.CompletedTask;
    }

    protected override async Task CreateEntityAsync()
    {
        if (_createForm.IsValid)
        {
            var createDto = MapToCreateInput(NewEntity);
            await AppService.CreateAsync(createDto);
            await _dataGrid.ReloadServerData();
            await CloseCreateModalAsync();
        }
    }

    protected override async Task OpenEditModalAsync(IdentityUserDto entity)
    {
        var userRoleNames = (await AppService.GetRolesAsync(entity.Id)).Items.Select(r => r.Name).ToList();
        EditUserRoles = Roles.Select(x => new AssignedRoleViewModel
        {
            Name = x.Name,
            IsAssigned = userRoleNames.Contains(x.Name)
        }).ToArray();
        EditingEntityId = entity.Id;
        EditingEntity = MapToEditingEntity(entity);
        _editDialogVisible = true;
    }

    protected override Task CloseEditModalAsync()
    {
        _editDialogVisible = false;
        return Task.CompletedTask;
    }

    protected override async Task UpdateEntityAsync()
    {
        if (_editForm.IsValid)
        {
            await AppService.UpdateAsync(EditingEntityId, EditingEntity);
            await _dataGrid.ReloadServerData();
            await CloseEditModalAsync();
        }
    }

    protected override async Task DeleteEntityAsync(IdentityUserDto entity)
    {
        if (await Message.Confirm(GetDeleteConfirmationMessage(entity)))
        {
            await base.DeleteEntityAsync(entity);
            await _dataGrid.ReloadServerData();
        }
    }

    protected override Task OnUpdatedEntityAsync()
    {
        return Task.CompletedTask;
    }

    protected virtual async Task OpenPermissionsModalAsync(IdentityUserDto entity)
    {
        await _permissionManagementModal.OpenDialogAsync(
            PermissionProviderName, entity.Id.ToString());
    }

    protected virtual DialogOptions DialogOptions
    {
        get => new()
        {
            CloseButton = true,
            CloseOnEscapeKey = true,
            DisableBackdropClick = true
        };
    }
}

Screenshots

image

image

Source Code

The source code of this project is available on Github