mirror of https://github.com/abpframework/abp
Merge pull request #16113 from abpframework/blazor-convert-modals-to-pages
Add Convert Create/Edit Modals to Page articlepull/16131/head
commit
37b59a7f05
@ -0,0 +1,372 @@
|
||||
# Convert Create/Edit Modals to Page
|
||||
In this document we will explain how to convert BookStore's Books create & edit modals to regular blazor pages.
|
||||
|
||||
## Before
|
||||

|
||||
|
||||
## After
|
||||
|
||||

|
||||
|
||||
|
||||
# Books.razor Page
|
||||
Books.razor page is the main page of the books management. Create & Update operations are done in this page. So we'll remove create & update operations from this page and move a separate blazor component for each operation. Each component will be a page.
|
||||
|
||||
- Remove both Create & Update modals.
|
||||
|
||||

|
||||
|
||||
- Replace **NewBook** button with a link to **CreateBook** page.
|
||||
|
||||
```html
|
||||
<Button Color="Color.Primary" Type="ButtonType.Link" To="books/new">
|
||||
@L["NewBook"]
|
||||
</Button>
|
||||
```
|
||||
|
||||
- Inject `NavigationManager` to `Books.razor` page.
|
||||
```csharp
|
||||
@inject NavigationManager NavigationManager
|
||||
```
|
||||
|
||||
- Replace **Edit** button with a link to **UpdateBook** page.
|
||||
|
||||
```html
|
||||
<Button Color="Color.Primary" Type="ButtonType.Link" OnClick="() => NavigateToEdit(book.Id)">
|
||||
@L["Edit"]
|
||||
</Button>
|
||||
```
|
||||
|
||||
```csharp
|
||||
private void NavigateToEdit(Guid id)
|
||||
{
|
||||
NavigationManager.NavigateTo($"books/{id}/edit");
|
||||
}
|
||||
```
|
||||
|
||||
- Remove all methods in the `Books.razor` page except constructor. And add `GoToEditPage` as below:
|
||||
|
||||
```csharp
|
||||
protected void GoToEditPage(BookDto book)
|
||||
{
|
||||
NavigationManager.NavigateTo($"books/{book.Id}");
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
- Change Edit button to a link in the table.
|
||||
|
||||
```html
|
||||
<EntityAction TItem="BookDto"
|
||||
Text="@L["Edit"]"
|
||||
Visible=HasUpdatePermission
|
||||
Clicked="() => GoToEditPage(context)" />
|
||||
```
|
||||
|
||||
|
||||
# CreateBooks Page
|
||||
Create new `CreateBook.razor` and `CreateBook.razor.cs` files in your project.
|
||||
|
||||
- `CreateBook.razor`
|
||||
|
||||
```html
|
||||
@page "/books/new"
|
||||
@attribute [Authorize(BookStorePermissions.Books.Create)]
|
||||
@inherits BookStoreComponentBase
|
||||
|
||||
@using Acme.BookStore.Books;
|
||||
@using Acme.BookStore.Localization;
|
||||
@using Acme.BookStore.Permissions;
|
||||
@using Microsoft.Extensions.Localization;
|
||||
@using Volo.Abp.AspNetCore.Components.Web;
|
||||
|
||||
@inject IStringLocalizer<BookStoreResource> L
|
||||
@inject AbpBlazorMessageLocalizerHelper<BookStoreResource> LH
|
||||
@inject IBookAppService AppService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<HeadContent>
|
||||
<ModalTitle>@L["NewBook"]</ModalTitle>
|
||||
</HeadContent>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
|
||||
<Validation MessageLocalizer="@LH.Localize">
|
||||
<Field>
|
||||
<FieldLabel>@L["Author"]</FieldLabel>
|
||||
<Select TValue="Guid" @bind-SelectedValue="@NewEntity.AuthorId">
|
||||
@foreach (var author in authorList)
|
||||
{
|
||||
<SelectItem TValue="Guid" Value="@author.Id">
|
||||
@author.Name
|
||||
</SelectItem>
|
||||
}
|
||||
</Select>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>@L["Name"]</FieldLabel>
|
||||
<TextEdit @bind-Text="@NewEntity.Name">
|
||||
<Feedback>
|
||||
<ValidationError />
|
||||
</Feedback>
|
||||
</TextEdit>
|
||||
</Field>
|
||||
</Validation>
|
||||
<Field>
|
||||
<FieldLabel>@L["Type"]</FieldLabel>
|
||||
<Select TValue="BookType" @bind-SelectedValue="@NewEntity.Type">
|
||||
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
|
||||
{
|
||||
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
|
||||
@L[$"Enum:BookType.{bookTypeValue}"]
|
||||
</SelectItem>
|
||||
}
|
||||
</Select>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>@L["PublishDate"]</FieldLabel>
|
||||
<DateEdit TValue="DateTime" @bind-Date="NewEntity.PublishDate" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>@L["Price"]</FieldLabel>
|
||||
<NumericEdit TValue="float" @bind-Value="NewEntity.Price" />
|
||||
</Field>
|
||||
</Validations>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button Color="Color.Secondary" Type="ButtonType.Link" To="books">
|
||||
@L["Cancel"]
|
||||
</Button>
|
||||
<Button Color="Color.Primary"
|
||||
Type="@ButtonType.Submit"
|
||||
PreventDefaultOnSubmit="true"
|
||||
Clicked="CreateEntityAsync">
|
||||
@L["Save"]
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
```
|
||||
|
||||
- `CreateBook.razor.cs`
|
||||
|
||||
```csharp
|
||||
using Acme.BookStore.Books;
|
||||
using Blazorise;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Volo.Abp;
|
||||
|
||||
namespace Acme.BookStore.Blazor.Pages;
|
||||
|
||||
public partial class CreateBook
|
||||
{
|
||||
protected Validations CreateValidationsRef;
|
||||
protected CreateUpdateBookDto NewEntity = new();
|
||||
IReadOnlyList<AuthorLookupDto> authorList = Array.Empty<AuthorLookupDto>();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
authorList = (await AppService.GetAuthorLookupAsync()).Items;
|
||||
|
||||
if (!authorList.Any())
|
||||
{
|
||||
throw new UserFriendlyException(message: L["AnAuthorIsRequiredForCreatingBook"]);
|
||||
}
|
||||
|
||||
NewEntity.AuthorId = authorList.First().Id;
|
||||
|
||||
if (CreateValidationsRef != null)
|
||||
{
|
||||
await CreateValidationsRef.ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task CreateEntityAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var validate = true;
|
||||
if (CreateValidationsRef != null)
|
||||
{
|
||||
validate = await CreateValidationsRef.ValidateAll();
|
||||
}
|
||||
if (validate)
|
||||
{
|
||||
await AppService.CreateAsync(NewEntity);
|
||||
NavigationManager.NavigateTo("books");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await HandleErrorAsync(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# EditBooks Page
|
||||
Create new `EditBook.razor` and `EditBook.razor.cs` files in your project.
|
||||
|
||||
- `EditBook.razor`
|
||||
|
||||
```html
|
||||
@page "/books/{Id}"
|
||||
@attribute [Authorize(BookStorePermissions.Books.Edit)]
|
||||
@inherits BookStoreComponentBase
|
||||
@using Acme.BookStore.Books;
|
||||
@using Acme.BookStore.Localization;
|
||||
@using Acme.BookStore.Permissions;
|
||||
@using Microsoft.Extensions.Localization;
|
||||
@using Volo.Abp.AspNetCore.Components.Web;
|
||||
|
||||
@inject IStringLocalizer<BookStoreResource> L
|
||||
@inject AbpBlazorMessageLocalizerHelper<BookStoreResource> LH
|
||||
@inject IBookAppService AppService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<HeadContent>
|
||||
<ModalTitle>@EditingEntity.Name</ModalTitle>
|
||||
</HeadContent>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Validations @ref="@EditValidationsRef" Model="@EditingEntity" ValidateOnLoad="false">
|
||||
<Validation MessageLocalizer="@LH.Localize">
|
||||
<Field>
|
||||
<FieldLabel>@L["Author"]</FieldLabel>
|
||||
<Select TValue="Guid" @bind-SelectedValue="@EditingEntity.AuthorId">
|
||||
@foreach (var author in authorList)
|
||||
{
|
||||
<SelectItem TValue="Guid" Value="@author.Id">
|
||||
@author.Name
|
||||
</SelectItem>
|
||||
}
|
||||
</Select>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>@L["Name"]</FieldLabel>
|
||||
<TextEdit @bind-Text="@EditingEntity.Name">
|
||||
<Feedback>
|
||||
<ValidationError />
|
||||
</Feedback>
|
||||
</TextEdit>
|
||||
</Field>
|
||||
</Validation>
|
||||
<Field>
|
||||
<FieldLabel>@L["Type"]</FieldLabel>
|
||||
<Select TValue="BookType" @bind-SelectedValue="@EditingEntity.Type">
|
||||
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
|
||||
{
|
||||
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
|
||||
@L[$"Enum:BookType.{bookTypeValue}"]
|
||||
</SelectItem>
|
||||
}
|
||||
</Select>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>@L["PublishDate"]</FieldLabel>
|
||||
<DateEdit TValue="DateTime" @bind-Date="EditingEntity.PublishDate" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>@L["Price"]</FieldLabel>
|
||||
<NumericEdit TValue="float" @bind-Value="EditingEntity.Price" />
|
||||
</Field>
|
||||
</Validations>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button Color="Color.Secondary" Type="ButtonType.Link" To="books">
|
||||
@L["Cancel"]
|
||||
</Button>
|
||||
<Button Color="Color.Primary"
|
||||
Type="@ButtonType.Submit"
|
||||
PreventDefaultOnSubmit="true"
|
||||
Clicked="UpdateEntityAsync">
|
||||
@L["Save"]
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
```
|
||||
|
||||
- `EditBook.razor.cs`
|
||||
|
||||
```csharp
|
||||
using Acme.BookStore.Books;
|
||||
using Blazorise;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Volo.Abp;
|
||||
|
||||
namespace Acme.BookStore.Blazor.Pages;
|
||||
|
||||
public partial class EditBook
|
||||
{
|
||||
protected CreateUpdateBookDto EditingEntity = new();
|
||||
protected Validations EditValidationsRef;
|
||||
IReadOnlyList<AuthorLookupDto> authorList = Array.Empty<AuthorLookupDto>();
|
||||
|
||||
[Parameter]
|
||||
public string Id { get; set; }
|
||||
|
||||
public Guid EditingEntityId { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
// Blazor can't parse Guid as route constraint currently.
|
||||
// See https://github.com/dotnet/aspnetcore/issues/19008
|
||||
EditingEntityId = Guid.Parse(Id);
|
||||
|
||||
authorList = (await AppService.GetAuthorLookupAsync()).Items;
|
||||
|
||||
if (!authorList.Any())
|
||||
{
|
||||
throw new UserFriendlyException(message: L["AnAuthorIsRequiredForCreatingBook"]);
|
||||
}
|
||||
|
||||
var entityDto = await AppService.GetAsync(EditingEntityId);
|
||||
|
||||
EditingEntity = ObjectMapper.Map<BookDto,CreateUpdateBookDto>(entityDto);
|
||||
|
||||
if (EditValidationsRef != null)
|
||||
{
|
||||
await EditValidationsRef.ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task UpdateEntityAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var validate = true;
|
||||
if (EditValidationsRef != null)
|
||||
{
|
||||
validate = await EditValidationsRef.ValidateAll();
|
||||
}
|
||||
if (validate)
|
||||
{
|
||||
await AppService.UpdateAsync(EditingEntityId, EditingEntity);
|
||||
|
||||
NavigationManager.NavigateTo("books");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await HandleErrorAsync(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can check the following commit for details:
|
||||
https://github.com/abpframework/abp-samples/commit/aae61ad6d66ebf6191dd4dcfb4e23d30bd680a4e
|
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 191 KiB |
After Width: | Height: | Size: 314 KiB |
Loading…
Reference in new issue