I'm sharing the updated Blazor Suite templates. You can also update them on your side
Item.razor.txt
@page "/%%entity-name-plural-kebabcase%%"
@attribute [Authorize(%%only-project-name%%Permissions.%%entity-name-plural%%.Default)]
@using %%project-name%%.%%<if:IsModule>%%%%only-project-name%%.%%</if:IsModule>%%%%entity-namespace%%
@using %%project-name%%.Localization
@using %%solution-namespace%%%%<if:ApplicationContractsNotExists>%%%%.AppServices%%</if:ApplicationContractsNotExists>%%.Shared
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Extensions.Localization
@using Blazorise.Components
@using Volo.Abp.BlazoriseUI.Components
@using Volo.Abp.ObjectMapping
@using Volo.Abp.AspNetCore.Components.Messages
@using Volo.Abp.AspNetCore.Components.Web.Theming.Layout
@using %%project-name%%.Permissions
@inherits %%only-project-name%%ComponentBase
@inject I%%entity-name-plural%%AppService %%entity-name-plural%%AppService
@inject IUiMessageService UiMessageService
%%enum-namespaces%%
@* ************************* PAGE HEADER ************************* *@
<PageHeader Title="@L["%%entity-name-plural%%"]" BreadcrumbItems="BreadcrumbItems" Toolbar="Toolbar">
</PageHeader>
@* ************************* SEARCH ************************* *@
<Card>
<CardBody>
<Form id="%%entity-name%%SearchForm" class="mb-3">
<Addons>
<Addon AddonType="AddonType.Body">
<TextEdit @bind-Text="@Filter.FilterText"
Autofocus="true"
Placeholder="@L["Search"]">
</TextEdit>
</Addon>
<Addon AddonType="AddonType.End">
<SubmitButton Form="%%entity-name%%SearchForm" Clicked="Get%%entity-name-plural%%Async">
<Icon Name="IconName.Search" Class="mr-1"></Icon>@L["Search"]
</SubmitButton>
</Addon>
</Addons>
</Form>
</CardBody>
</Card>
@* ************************* DATA GRID ************************* *@
<Card>
<CardBody>
<DataGrid TItem="%%dto-type%%"
Data="%%entity-name%%List"
ReadData="OnDataGridReadAsync"
TotalItems="TotalCount"
ShowPager="true"
Responsive="true"
PageSize="PageSize">
<DataGridColumns>
<DataGridEntityActionsColumn TItem="%%dto-type%%" @ref="@EntityActionsColumn">
<DisplayTemplate>
<EntityActions TItem="%%dto-type%%" EntityActionsColumn="@EntityActionsColumn">
<EntityAction TItem="%%dto-type%%"
Visible="@CanEdit%%entity-name%%"
Clicked="() => OpenEdit%%entity-name%%Modal(context)"
Text="@L["Edit"]"></EntityAction>
<EntityAction TItem="%%dto-type%%"
Visible="@CanDelete%%entity-name%%"
Clicked="() => Delete%%entity-name%%Async(context)"
ConfirmationMessage="@(()=> L["DeleteConfirmationMessage"])"
Text="@L["Delete"]"></EntityAction>
</EntityActions>
</DisplayTemplate>
</DataGridEntityActionsColumn>
%%razor-datagrid-columns%%
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>
@* ************************* CREATE MODAL ************************* *@
<Modal @ref="Create%%entity-name%%Modal">
<ModalContent Centered="true">
<Form id="Create%%entity-name%%Form">
<ModalHeader>
<ModalTitle>@L["New%%entity-name%%"]</ModalTitle>
<CloseButton Clicked="CloseCreate%%entity-name%%Modal" />
</ModalHeader>
<ModalBody>
<Validations @ref="@New%%entity-name%%Validations"
Mode="ValidationMode.Auto"
Model="@New%%entity-name%%"
ValidateOnLoad="false">
%%razor-modal-fields-create%%
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseCreate%%entity-name%%Modal">
@L["Cancel"]
</Button>
<SubmitButton Form="Create%%entity-name%%Form" Clicked="Create%%entity-name%%Async" />
</ModalFooter>
</Form>
</ModalContent>
</Modal>
@* ************************* EDIT MODAL ************************* *@
<Modal @ref="Edit%%entity-name%%Modal">
<ModalContent Centered="true">
<Form id="Edit%%entity-name%%Form">
<ModalHeader>
<ModalTitle>@L["Update"]</ModalTitle>
<CloseButton Clicked="CloseEdit%%entity-name%%Modal" />
</ModalHeader>
<ModalBody>
<Validations @ref="@Editing%%entity-name%%Validations"
Mode="ValidationMode.Auto"
Model="@Editing%%entity-name%%"
ValidateOnLoad="false">
%%razor-modal-fields-edit%%
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseEdit%%entity-name%%Modal">
@L["Cancel"]
</Button>
<SubmitButton Form="Create%%entity-name%%Form" Clicked="Update%%entity-name%%Async" />
</ModalFooter>
</Form>
</ModalContent>
</Modal>
Item.razor_cs.txt:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Blazorise;
using Blazorise.DataGrid;
using Volo.Abp.BlazoriseUI.Components;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars;
using %%project-name%%.%%entity-namespace%%;
using %%project-name%%.Permissions;
using %%project-name%%.Shared;
namespace %%solution-namespace%%.Blazor.Pages
{
public partial class %%entity-name-plural%%
{
protected List<Volo.Abp.BlazoriseUI.BreadcrumbItem> BreadcrumbItems = new List<Volo.Abp.BlazoriseUI.BreadcrumbItem>();
protected PageToolbar Toolbar {get;} = new PageToolbar();
private IReadOnlyList<%%dto-name%%> %%entity-name%%List { get; set; }
private int PageSize { get; } = LimitedResultRequestDto.DefaultMaxResultCount;
private int CurrentPage { get; set; } = 1;
private string CurrentSorting { get; set; }
private int TotalCount { get; set; }
private bool CanCreate%%entity-name%% { get; set; }
private bool CanEdit%%entity-name%% { get; set; }
private bool CanDelete%%entity-name%% { get; set; }
private %%entity-name%%CreateDto New%%entity-name%% { get; set; }
private Validations New%%entity-name%%Validations { get; set; }
private %%entity-name%%UpdateDto Editing%%entity-name%% { get; set; }
private Validations Editing%%entity-name%%Validations { get; set; }
private %%primary-key%% Editing%%entity-name%%Id { get; set; }
private Modal Create%%entity-name%%Modal { get; set; }
private Modal Edit%%entity-name%%Modal { get; set; }
private Get%%entity-name-plural%%Input Filter { get; set; }
private DataGridEntityActionsColumn<%%dto-name%%> EntityActionsColumn { get; set; }
%%navigation-properties%%
public %%entity-name-plural%%()
{
New%%entity-name%% = new %%entity-name%%CreateDto();
Editing%%entity-name%% = new %%entity-name%%UpdateDto();
Filter = new Get%%entity-name-plural%%Input
{
MaxResultCount = PageSize,
SkipCount = (CurrentPage - 1) * PageSize,
Sorting = CurrentSorting
};
}
protected override async Task OnInitializedAsync()
{
await SetToolbarItemsAsync();
await SetBreadcrumbItemsAsync();
await SetPermissionsAsync();%%navigation-property-select-load-data%%
}
protected virtual ValueTask SetBreadcrumbItemsAsync()
{
BreadcrumbItems.Add(new Volo.Abp.BlazoriseUI.BreadcrumbItem(L["Menu:%%entity-name-plural%%"]));
return ValueTask.CompletedTask;
}
protected virtual ValueTask SetToolbarItemsAsync()
{
Toolbar.AddButton(L["New%%entity-name%%"], () =>
{
OpenCreate%%entity-name%%Modal();
return Task.CompletedTask;
}, IconName.Add, requiredPolicyName: %%only-project-name%%Permissions.%%entity-name-plural%%.Create);
return ValueTask.CompletedTask;
}
private async Task SetPermissionsAsync()
{
CanCreate%%entity-name%% = await AuthorizationService
.IsGrantedAsync(%%only-project-name%%Permissions.%%entity-name-plural%%.Create);
CanEdit%%entity-name%% = await AuthorizationService
.IsGrantedAsync(%%only-project-name%%Permissions.%%entity-name-plural%%.Edit);
CanDelete%%entity-name%% = await AuthorizationService
.IsGrantedAsync(%%only-project-name%%Permissions.%%entity-name-plural%%.Delete);
}
private async Task Get%%entity-name-plural%%Async()
{
Filter.MaxResultCount = PageSize;
Filter.SkipCount = (CurrentPage - 1) * PageSize;
Filter.Sorting = CurrentSorting;
var result = await %%entity-name-plural%%AppService.GetListAsync(Filter);
%%entity-name%%List = result.Items;
TotalCount = (int)result.TotalCount;
}
protected virtual async Task SearchAsync()
{
CurrentPage = 1;
await Get%%entity-name-plural%%Async();
await InvokeAsync(StateHasChanged);
}
private async Task OnDataGridReadAsync(DataGridReadDataEventArgs<%%dto-name%%> e)
{
CurrentSorting = e.Columns
.Where(c => c.Direction != SortDirection.None)
.Select(c => c.Field + (c.Direction == SortDirection.Descending ? " DESC" : ""))
.JoinAsString(",");
CurrentPage = e.Page;
await Get%%entity-name-plural%%Async();
await InvokeAsync(StateHasChanged);
}
private void OpenCreate%%entity-name%%Modal()
{
New%%entity-name%% = new %%entity-name%%CreateDto{
%%create-modal-date-initial-values%%
%%create-modal-required-np-initial-values%%
};
New%%entity-name%%Validations.ClearAll();
Create%%entity-name%%Modal.Show();
}
private void CloseCreate%%entity-name%%Modal()
{
Create%%entity-name%%Modal.Hide();
}
private void OpenEdit%%entity-name%%Modal(%%dto-name%% input)
{
Editing%%entity-name%%Id = input%%entity-name-for-navigation-property%%.Id;
Editing%%entity-name%% = ObjectMapper.Map<%%entity-name%%Dto, %%entity-name%%UpdateDto>(input%%entity-name-for-navigation-property%%);
Editing%%entity-name%%Validations.ClearAll();
Edit%%entity-name%%Modal.Show();
}
private async Task Delete%%entity-name%%Async(%%dto-name%% input)
{
await %%entity-name-plural%%AppService.DeleteAsync(input%%entity-name-for-navigation-property%%.Id);
await Get%%entity-name-plural%%Async();
}
private async Task Create%%entity-name%%Async()
{
try
{
if (New%%entity-name%%Validations?.ValidateAll() == false)
{
return;
}
await %%entity-name-plural%%AppService.CreateAsync(New%%entity-name%%);
await Get%%entity-name-plural%%Async();
Create%%entity-name%%Modal.Hide();
}
catch (Exception ex)
{
await HandleErrorAsync(ex);
}
}
private void CloseEdit%%entity-name%%Modal()
{
Edit%%entity-name%%Modal.Hide();
}
private async Task Update%%entity-name%%Async()
{
try
{
if (Editing%%entity-name%%Validations?.ValidateAll() == false)
{
return;
}
await %%entity-name-plural%%AppService.UpdateAsync(Editing%%entity-name%%Id, Editing%%entity-name%%);
await Get%%entity-name-plural%%Async();
Edit%%entity-name%%Modal.Hide();
}
catch (Exception ex)
{
await HandleErrorAsync(ex);
}
}
%%navigation-property-lookup-methods%%
}
}
your domain service should depend on the EntityFrameworkCore.Test project. and your EntityFrameworkCore.Test project module class should be like this
You can create a new project from module template, to see how it works
abp new Acme.MyProModule -t module-pro -csf
[DependsOn(
typeof(MyProjectNameTestBaseModule),
typeof(MyProjectNameEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class MyProjectNameEntityFrameworkCoreTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var sqliteConnection = CreateDatabaseAndGetConnection();
Configure<AbpDbContextOptions>(options =>
{
options.Configure(abpDbContextConfigurationContext =>
{
abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection);
});
});
}
private static SqliteConnection CreateDatabaseAndGetConnection()
{
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
new MyProjectNameDbContext(
new DbContextOptionsBuilder<MyProjectNameDbContext>().UseSqlite(connection).Options
).GetService<IRelationalDatabaseCreator>().CreateTables();
return connection;
}
}
see https://support.abp.io/QA/Questions/1607/About-Many-to-Many-relationship-for-Different-Module-template https://github.com/abpframework/abp/issues/7689
also download the EasyCRM application which is made with ABP. you can see many to many relationship example https://docs.abp.io/en/commercial/latest/samples/easy-crm
we'll try to reproduce it.
hi I've created an internal issue for this enhancement (issue no: 7639) probably it'll be implmented in 5.0
hi,
there was an error on 4.4.1. do you get the same issue with 4.4.2?
the directory paths are stored in the following locations:
%UserProfile%\.abp\suite\appsettings.json (WINDOWS)~/.abp/suite/appsettings.json (MAC)check that the paths are correct and valid for your ABP solution.
can you reproduce it with 4.4.2?