Open Closed

Blazor Server Create (and Update) Modal Validation for Required Field #1803


User avatar
0
cellero created
  • ABP Framework version: v4.4.0
  • UI type: Blazor Server
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no

The create modal crashes when a required field is left empty. The bug is easy to replicate

  1. Use Abp Suite to create a simple entity with a required field/property
  2. Run the Blazor app and click on the "+ New xx" button
  3. Press save without completing any fields
  4. The error will occur.

Note that on update, the UI does provide feedback, but still crashes when pressing the save button.

Please advise the correct approach for solving this issue and can confirm it is a bug to be solved.

Thanks.


6 Answer(s)
  • User Avatar
    0
    alper created
    Support Team Director

    we'll try to reproduce it.

  • User Avatar
    0
    EngincanV created
    Support Team .NET Developer

    Hi @cellero, please wrap your create and update methods with try-catch blocks and handle exception by yourself (by using HandleErrorAsync method). => https://support.abp.io/QA/Questions/1523/Blazor-Server-ABP-Exception-Dialog-not-shown#answer-7939465b-3349-3100-2cc1-39fd4fd8c78a

    private async Task CreateBookAsync()
    {
       try
       {
          await BooksAppService.CreateAsync(NewBook);
          await GetBooksAsync();
          CreateBookModal.Hide();
       }
       catch (Exception ex)
       {
          await HandleErrorAsync(ex);
       }
    }
    
    • Please also be sure about your *.razor page inherits from your base component.
    @page "/books"
    @attribute [Authorize(MyPermissions.Books.Default)]
    @inherits MyPageComponentBase //be sure about your razor page inherits from your base component
    ///...
    
  • User Avatar
    0
    cellero created

    Thank you for your prompt reply. I have implemented this work around. Hopefully this is being worked on to be handled by the ABP suite.

  • User Avatar
    0
    alper created
    Support Team Director

    thanks for the feedback. this will be fixed in the next version. meantime you can add the marked code block to show the validation error

  • User Avatar
    0
    alper created
    Support Team Director

    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%%
        }
    }
    
    
  • User Avatar
    0
    cellero created

    Perfect. thank you.

Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11