fix(module: modal): throwing exceprion when navigation after open (#4015)

* fix(module: modal): throwing exceprion after open and navigation

* use modal service instead

* fix close

* fix loading update

* fix confirm template callback

* just close modal which was created by service during navigation

* clean
This commit is contained in:
James Yeung 2024-07-25 08:10:46 +08:00 коммит произвёл GitHub
Родитель bcce6df3a8
Коммит 49cadeebca
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
13 изменённых файлов: 170 добавлений и 282 удалений

Просмотреть файл

@ -15,7 +15,9 @@ namespace AntDesign
/// <summary>
/// trigger after Dialog is closed
/// </summary>
public Func<Task> OnClosed { get; set; }
public Func<Task> AfterClose { get; set; } = () => Task.CompletedTask;
public Func<Task> AfterOpen { get; set; } = () => Task.CompletedTask;
/// <summary>
/// ant-modal-body style
@ -45,7 +47,26 @@ namespace AntDesign
/// <summary>
/// Whether to apply loading visual effect for OK button or not
/// </summary>
public bool ConfirmLoading { get; set; }
public bool ConfirmLoading
{
get
{
if (OkButtonProps != null)
{
return OkButtonProps.Loading;
}
return false;
}
set
{
if (OkButtonProps == null)
{
OkButtonProps = new ButtonProps();
}
OkButtonProps.Loading = value;
}
}
/// <summary>
/// modal header
@ -65,7 +86,7 @@ namespace AntDesign
/// <summary>
/// ChildContent
/// </summary>
public RenderFragment ChildContent { get; set; }
public RenderFragment Content { get; set; }
/// <summary>
/// the class name of the element of ".ant-modal"
@ -76,13 +97,13 @@ namespace AntDesign
/// for OK-Cancel Confirm dialog, cancel button clicked callback.
/// It's only trigger in Confirm created by ModalService mode
/// </summary>
public Func<MouseEventArgs, Task> OnCancel { get; set; }
public virtual Func<MouseEventArgs, Task> OnCancel { get; set; }
/// <summary>
/// for OK-Cancel Confirm dialog, OK button clicked callback.
/// It's only trigger in Confirm created by ModalService mode
/// </summary>
public Func<MouseEventArgs, Task> OnOk { get; set; }
public virtual Func<MouseEventArgs, Task> OnOk { get; set; }
/// <summary>
/// max modal body content height

Просмотреть файл

@ -9,8 +9,35 @@ namespace AntDesign
/// <summary>
/// The options of Modal dialog box
/// </summary>
public class ModalOptions : DialogOptionsBase
public class ModalOptions : DialogOptions
{
private Func<MouseEventArgs, Task> _onCancel;
private Func<MouseEventArgs, Task> _onOk;
internal ModalRef ModalRef { get; set; }
/// <summary>
/// ant-modal style
/// </summary>
public string Style { get; set; }
/// <summary>
///
/// </summary>
public bool Visible { get; set; } = true;
/// <summary>
/// Specify a function that will be called when a user clicks mask, close button on top right or Cancel button.
/// </summary>
public override Func<MouseEventArgs, Task> OnCancel { get => _onCancel; set => _onCancel = value; }
/// <summary>
/// Specify a function that will be called when a user clicks the OK button
/// </summary>
public override Func<MouseEventArgs, Task> OnOk { get => _onOk; set => _onOk = value; }
public ModalOptions()
{
_onCancel = DefaultOnCancelOrOk;
@ -19,138 +46,9 @@ namespace AntDesign
MaskClosable = true;
}
internal ModalRef ModalRef { get; set; }
/// <summary>
/// trigger after Dialog is closed
/// </summary>
public Func<Task> AfterClose { get; set; } = () => Task.CompletedTask;
/// <summary>
/// ant-modal style
/// </summary>
public string Style { get; set; }
/// <summary>
/// ant-modal-body style
/// </summary>
public string BodyStyle { get; set; }
/// <summary>
/// show ant-modal-closer
/// </summary>
public bool Closable { get; set; } = true;
/// <summary>
/// Draggable modal
/// </summary>
public bool Draggable { get; set; }
/// <summary>
/// Drag and drop only within the Viewport
/// </summary>
public bool DragInViewport { get; set; } = true;
/// <summary>
/// closer icon RenderFragment, the default is a "X"
/// </summary>
public RenderFragment CloseIcon { get; set; } = DialogOptions.DefaultCloseIcon;
/// <summary>
/// Whether to apply loading visual effect for OK button or not
/// </summary>
public bool ConfirmLoading
{
get
{
if (OkButtonProps != null)
{
return OkButtonProps.Loading;
}
return false;
}
set
{
if (OkButtonProps == null)
{
OkButtonProps = new ButtonProps();
}
OkButtonProps.Loading = value;
}
}
/// <summary>
/// Whether to remove Modal from DOM after the Modal closed
/// </summary>
public bool DestroyOnClose { get; set; }
/// <summary>
/// Modal footer. If Footer==null, the dialog will not have a footer
/// </summary>
public OneOf<string, RenderFragment>? Footer { get; set; } = DialogOptions.DefaultFooter;
/// <summary>
///
/// </summary>
public bool Visible { get; set; } = true;
/// <summary>
/// The class name of the container of the modal dialog
/// </summary>
public string WrapClassName { get; set; }
private Func<MouseEventArgs, Task> _onCancel;
/// <summary>
/// Specify a function that will be called when a user clicks mask, close button on top right or Cancel button.
/// </summary>
public Func<MouseEventArgs, Task> OnCancel { get => _onCancel; set => _onCancel = value; }
private Func<MouseEventArgs, Task> _onOk;
/// <summary>
/// Specify a function that will be called when a user clicks the OK button
/// </summary>
public Func<MouseEventArgs, Task> OnOk { get => _onOk; set => _onOk = value; }
/// <summary>
/// ChildContent
/// </summary>
public RenderFragment Content { get; set; } = null;
/// <summary>
/// show modal maximize button
/// </summary>
public bool Maximizable { get; set; } = false;
/// <summary>
/// The icon of the maximize button when the modal is in normal state
/// </summary>
public RenderFragment MaximizeBtnIcon { get; set; } = DialogOptions.DefaultMaximizeIcon;
/// <summary>
/// The icon of the maximize button when the modal is maximized
/// </summary>
public RenderFragment RestoreBtnIcon { get; set; } = DialogOptions.DefaultRestoreIcon;
/// <summary>
/// Maximize the Modal during component initialization, and it will ignore the Maximizable value.
/// </summary>
public bool DefaultMaximized { get; set; } = false;
/// <summary>
/// Resizable
/// </summary>
public bool Resizable { get; set; }
#region internal
public async Task DefaultOnCancelOrOk(MouseEventArgs e)
{
await (ModalRef?.CloseAsync() ?? Task.CompletedTask);
}
#endregion
}
}

Просмотреть файл

@ -69,7 +69,7 @@ namespace AntDesign
config.ClassName = "ant-modal-confirm ant-modal-confirm-" + confirmOptions.ConfirmType;
config.Title = null;
config.CloseIcon = null;
config.OnClosed = Close;
config.AfterClose = Close;
config.OnCancel = ConfirmRef.Config.CreateByService ? e => HandleCancel(e, ConfirmResult.Cancel) : new Func<MouseEventArgs, Task>(async (e) => await Close());
return config;
}

Просмотреть файл

@ -302,7 +302,7 @@ namespace AntDesign
public Task<ConfirmRef<TResult>> CreateConfirmAsync<TComponent, TComponentOptions, TResult>(ConfirmOptions config, TComponentOptions componentOptions) where TComponent : FeedbackComponent<TComponentOptions, TResult>
{
CheckConfirmOptionsIsNull(config);
config.CreateByService = true;
ConfirmRef<TResult> confirmRef = new ConfirmRef<TResult>(config, this);
OnConfirmOpenEvent?.Invoke(confirmRef);

Просмотреть файл

@ -19,7 +19,7 @@
style="@GetStyle()"
>
<div id="@_sentinelStart" tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div>
<div class=@($"{Config.PrefixCls}-content") style="@(Config.Resizable ? "resize:horizontal; overflow: hidden;":"")" id=@($"{Config.PrefixCls}-wrap_{DialogWrapperId}")>
<div class=@($"{Config.PrefixCls}-content") style="@(Config.Resizable ? "resize:horizontal; overflow: hidden;":"")" id=@($"{Config.PrefixCls}-wrap_{Id}")>
@if (Config.Header != null)
{
<CascadingValue Value="@Config">
@ -31,7 +31,9 @@
</CascadingValue>
}
<div class=@($"{Config.PrefixCls}-body") style="@GetBodyStyle()">
@ChildContent
<CascadingValue Value=@($"#{Config.PrefixCls}-wrap_{Id}") Name="PopupContainerSelector" IsFixed>
@ChildContent
</CascadingValue>
</div>
@if (Config.Footer != null)
{

Просмотреть файл

@ -350,9 +350,9 @@ namespace AntDesign
_maskHideClsName = "ant-modal-mask-hidden";
await InvokeStateHasChangedAsync();
if (Config.OnClosed != null)
if (Config.AfterClose != null)
{
await Config.OnClosed.Invoke();
await Config.AfterClose.Invoke();
}
}
}
@ -440,8 +440,15 @@ namespace AntDesign
if (_hasDestroy)
{
_hasDestroy = false;
await AppendToContainer();
//await AppendToContainer();
Show();
CallAfterRender(async () =>
{
if (Config.AfterOpen != null)
{
await Config.AfterOpen.Invoke();
}
});
await InvokeStateHasChangedAsync();
}
@ -492,7 +499,7 @@ namespace AntDesign
if (!Config.CreateByService && !Config.DestroyOnClose)
{
_ = JsInvokeAsync(JSInteropConstants.DelElementFrom, "#" + Id, Config.GetContainer);
//_ = JsInvokeAsync(JSInteropConstants.DelElementFrom, "#" + Id);
}
}
}

Просмотреть файл

@ -1,20 +1,28 @@
@namespace AntDesign
@using Microsoft.AspNetCore.Components.Rendering
@inherits AntDomComponentBase
<DialogWrapper @ref="_dialogWrapper"
Config="@BuildDialogOptions()"
Visible="@Visible"
OnAfterShow="@OnAfterDialogShow"
OnAfterHide="@OnAfterHide"
OnBeforeDestroy="@OnBeforeDialogWrapperDestroy"
Style="@Style"
Class="@Class"
Id="@Id">
<CascadingValue Value=@($"#ant-modal-wrap_{_dialogWrapper.Id}") Name="PopupContainerSelector">
<CascadingValue Value="true" Name="InModal">
<CascadingValue Value="@_dialogWrapper.Dialog.IsShow()" Name="ModalCompleteShow">
@ChildContent
</CascadingValue>
</CascadingValue>
</CascadingValue>
</DialogWrapper>
@code {
internal static RenderFragment GetModalRender(ModalRef modalRef)
{
var options = modalRef.Config;
var attributes = new Dictionary<string, object>()
{
["OnOk"] = EventCallback.Factory.Create(modalRef, options.OnOk),
["OnCancel"] = EventCallback.Factory.Create(modalRef, options.OnCancel),
};
void GetRender(RenderTreeBuilder __builder)
{
<Dialog @ref="modalRef.Dialog" Config="@options"
Visible="@options.Visible"
Style="@options.Style"
Class="@options.ClassName"
>
@options.Content
</Dialog>
}
return GetRender;
}
}

Просмотреть файл

@ -12,16 +12,10 @@ namespace AntDesign
/// <summary>
/// Modal Dialog
/// </summary>
public partial class Modal
public partial class Modal: AntDomComponentBase
{
#region Parameter
/// <summary>
///
/// </summary>
[Parameter]
public ModalRef ModalRef { get; set; }
/// <summary>
/// Specify a function that will be called when modal is closed
/// </summary>
@ -223,11 +217,6 @@ namespace AntDesign
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Is RTL
/// </summary>
public bool Rtl => base.RTL;
/// <summary>
/// Modal Locale
/// </summary>
@ -270,21 +259,23 @@ namespace AntDesign
[Parameter]
public bool Resizable { get; set; } = false;
public ModalRef ModalRef => _modalRef;
#endregion Parameter
#pragma warning disable 649
private DialogWrapper _dialogWrapper;
#pragma warning restore 649
[Inject] private ModalService ModalService { get; set; }
private DialogOptions BuildDialogOptions()
private ModalOptions BuildDialogOptions()
{
DialogOptions options = new DialogOptions()
ModalOptions options = new ModalOptions()
{
OnClosed = AfterClose,
AfterClose = AfterClose,
AfterOpen = OnAfterDialogShow,
BodyStyle = BodyStyle,
CancelText = CancelText ?? Locale.CancelText,
Centered = Centered,
Closable = Closable,
Content = ChildContent,
Draggable = Draggable,
DragInViewport = DragInViewport,
DestroyOnClose = DestroyOnClose,
@ -292,13 +283,11 @@ namespace AntDesign
ConfirmLoading = ConfirmLoading,
Header = Header,
Footer = Footer,
GetContainer = GetContainer,
Keyboard = Keyboard,
Mask = Mask,
MaskClosable = MaskClosable,
MaskStyle = MaskStyle,
OkText = OkText ?? Locale.OkText,
OkType = OkType,
Title = Title,
@ -310,13 +299,14 @@ namespace AntDesign
{
var args = new ModalClosingEventArgs(e, false);
var modalTemplate = (ModalRef as IFeedbackRef)?.ModalTemplate;
var modalTemplate = (_modalRef as IFeedbackRef)?.ModalTemplate;
if (modalTemplate != null)
await modalTemplate.OnFeedbackCancelAsync(args);
if (!args.Cancel)
{
await (ModalRef?.OnCancel?.Invoke() ?? Task.CompletedTask);
await (_modalRef?.OnCancel?.Invoke() ?? Task.CompletedTask);
await _modalRef.CloseAsync();
if (VisibleChanged.HasDelegate)
{
await VisibleChanged.InvokeAsync(false);
@ -332,22 +322,23 @@ namespace AntDesign
{
var args = new ModalClosingEventArgs(e, false);
var modalTemplate = (ModalRef as IFeedbackRef)?.ModalTemplate;
var modalTemplate = (_modalRef as IFeedbackRef)?.ModalTemplate;
if (modalTemplate != null)
await modalTemplate.OnFeedbackOkAsync(args);
if (!args.Cancel)
{
await (ModalRef?.OnOk?.Invoke() ?? Task.CompletedTask);
if (VisibleChanged.HasDelegate)
{
await VisibleChanged.InvokeAsync(false);
}
await (_modalRef?.OnOk?.Invoke() ?? Task.CompletedTask);
if (OnOk.HasDelegate)
{
await OnOk.InvokeAsync(e);
}
await _modalRef.CloseAsync();
if (VisibleChanged.HasDelegate)
{
await VisibleChanged.InvokeAsync(false);
}
}
else
{
@ -356,16 +347,15 @@ namespace AntDesign
}
},
OkButtonProps = OkButtonProps,
CancelButtonProps = CancelButtonProps,
Rtl = Rtl,
Rtl = base.RTL,
MaxBodyHeight = MaxBodyHeight,
Maximizable = Maximizable,
MaximizeBtnIcon = MaximizeBtnIcon,
RestoreBtnIcon = RestoreBtnIcon,
DefaultMaximized = DefaultMaximized,
Resizable = Resizable,
CreateByService = ModalRef?.Config.CreateByService ?? false,
CreateByService = false,
};
return options;
@ -377,15 +367,49 @@ namespace AntDesign
private bool _firstShow = true;
private ModalRef _modalRef;
protected override void OnInitialized()
{
var options = BuildDialogOptions();
_modalRef = new ModalRef(options, ModalService);
base.OnInitialized();
}
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.IsParameterChanged(nameof(ConfirmLoading), ConfirmLoading, out var newVal))
{
_modalRef.SetConfirmLoading(newVal);
}
var visibleChanged = parameters.IsParameterChanged(nameof(Visible), Visible, out var newVisible);
await base.SetParametersAsync(parameters);
if (visibleChanged)
{
if (Visible)
{
await _modalRef?.OpenAsync();
}
else
{
await _modalRef?.CloseAsync();
}
}
}
private async Task OnAfterDialogShow()
{
if (!_hasFocus)
{
await JsInvokeAsync(JSInteropConstants.FocusDialog, $"#{_dialogWrapper.Dialog.SentinelStart}");
await JsInvokeAsync(JSInteropConstants.FocusDialog, $"#{_modalRef.Dialog.SentinelStart}");
_hasFocus = true;
if (ModalRef?.OnOpen != null)
if (_modalRef?.OnOpen != null)
{
await ModalRef.OnOpen();
await _modalRef.OnOpen();
}
}
}
@ -393,9 +417,9 @@ namespace AntDesign
private async Task OnAfterHide()
{
_hasFocus = false;
if (ModalRef?.OnClose != null)
if (_modalRef?.OnClose != null)
{
await ModalRef.OnClose();
await _modalRef.OnClose();
}
}

Просмотреть файл

@ -4,45 +4,5 @@
@foreach (ModalRef modalRef in _modalRefs)
{
var options = modalRef.Config;
<Modal @key="@options"
ModalRef="@modalRef"
AfterClose="@options.AfterClose"
Style="@options.Style"
BodyStyle="@options.BodyStyle"
CancelText="@options.CancelText"
Centered="@options.Centered"
Closable="@options.Closable"
Draggable="@options.Draggable"
DragInViewport="@options.DragInViewport"
CloseIcon="@options.CloseIcon"
ConfirmLoading="@options.ConfirmLoading"
DestroyOnClose="@options.DestroyOnClose"
Footer="@options.Footer"
GetContainer="@options.GetContainer"
Keyboard="@options.Keyboard"
Mask="@options.Mask"
MaskClosable="@options.MaskClosable"
MaskStyle="@options.MaskStyle"
OkText="@options.OkText"
OkType="@options.OkType"
Title="@options.Title"
TitleTemplate="@options.TitleTemplate"
Visible="@options.Visible"
Width="@options.Width"
WrapClassName="@options.WrapClassName"
ZIndex="@options.ZIndex"
OnCancel="@options.OnCancel"
OnOk="@options.OnOk"
OkButtonProps="@options.OkButtonProps"
CancelButtonProps="@options.CancelButtonProps"
Maximizable="@options.Maximizable"
MaximizeBtnIcon="@options.MaximizeBtnIcon"
RestoreBtnIcon="@options.RestoreBtnIcon"
DefaultMaximized="@options.DefaultMaximized"
Resizable="@options.Resizable"
>
@options.Content
</Modal>
@modalRef.Render;
}

Просмотреть файл

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
@ -27,7 +28,7 @@ namespace AntDesign
private void OnLocationChanged(object sender, EventArgs e)
{
_modalRefs.Clear();
_modalRefs.RemoveAll(x => x.Config.CreateByService);
InvokeStateHasChanged();
}

Просмотреть файл

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace AntDesign
{
@ -11,6 +12,7 @@ namespace AntDesign
public class ModalRef : FeedbackRefWithOkCancelBase
{
public ModalOptions Config { get; private set; }
internal Dialog Dialog { get; set; }
private readonly ModalService _service;
internal ModalRef(ModalOptions config, ModalService modalService)
@ -19,6 +21,8 @@ namespace AntDesign
_service = modalService;
}
internal RenderFragment Render => Modal.GetModalRender(this);
/// <summary>
/// open the Modal dialog
/// </summary>
@ -29,11 +33,9 @@ namespace AntDesign
{
Config.Visible = true;
}
await _service.CreateOrOpenModalAsync(this);
}
/// <summary>
/// close the Modal dialog
/// </summary>
@ -58,6 +60,10 @@ namespace AntDesign
/// <param name="loading"></param>
public void SetConfirmLoading(bool loading)
{
if (Config.ConfirmLoading == loading)
{
return;
}
Config.ConfirmLoading = loading;
_service.UpdateModal(this);
}

Просмотреть файл

@ -40,7 +40,7 @@
_visible = false;
}
private async Task OpenModal()
private void OpenModal()
{
var options = new ModalOptions()
{
@ -55,8 +55,6 @@
await options.DefaultOnCancelOrOk(e);
};
await modalService.CreateModalAsync(options);
modalService.CreateModal(options);
}
}

Просмотреть файл

@ -1,37 +0,0 @@
@page "/test"
<Input TValue="string" Bordered="false" Class="methods__inputNumber" />
<AutoComplete @bind-Value="@value" Options="@options" OnSelectionChange="OnSelectionChange" OnActiveChange="OnActiveChange" Placeholder="input here" Class="auto" />
<Divider></Divider>
<span>bind-Value:@value</span>
<br />
<span>SelectedValue:@selectItem?.Value</span>
<br />
<span>ActiveValue:@activeItem?.Value</span>
@code
{
private string value;
List<string> options = new List<string>()
{
"Beijing","Shanghai","Guangzhou","Shenzhen","Chongqing","Wuhan"
};
private AutoCompleteOption selectItem;
void OnSelectionChange(AutoCompleteOption item)
{
selectItem = item;
}
private AutoCompleteOption activeItem;
void OnActiveChange(AutoCompleteOption item)
{
activeItem = item;
}
}