目录
介绍
为了设置本文的上下文,自Blazor首次发布以来,已经有许多关于如何处理编辑表单的讨论、文章和建议。特别是在离开脏表单时如何停止或至少警告用户。这个问题并非特定于Blazor:所有单页应用程序和网站都面临相同的挑战。
在经典的Web表单中,每个导航都是获取或发布回服务器的信息。我们可以使用内置window.beforeunload事件来警告用户他们页面上有未保存的数据。不太好,但至少有一些好处——我们将在以后使用。这项技术在SPA中不适用。在外人看来,导航事件不是。NavigationManager拦截页面中的任何导航尝试,触发其自己的LocationChanged事件并下沉请求。连接到这个事件中的Router完成了它的魔法,并加载新的组件集到页面中。没有真正的浏览器导航发生,因此浏览器没有任何beforeunload事件可捕获。
由程序员决定编写代码来阻止用户离开肮脏的表格。当您的应用程序依赖于URL导航借口时,说起来容易做起来难。工具栏、导航侧栏和许多按钮会提交URL以便在应用程序中导航。考虑现成的Blazor模板。左侧导航栏中的所有链接都位于顶部栏中。
就个人而言,我对整个路由难题都存在严重的问题:SPA是一种应用程序,而不是网站,但是我认为其必须是其中的一小部分!本文适用于大多数。
我遇到的所有NetCore 3.1解决方案都是一种形状或另一种形状的杂物,我自己创建了一个以上的形状。社区希望的是NetCore 5中的更改,特别是一些NavigationManager取消或阻止导航请求的额外功能。那没有发生:我不认为团队对于正确的解决方案有共识,所以我们回到正题。
我在本文中介绍的是我针对该问题的最新方法。它不是完美的,但我认为在获得一些新的浏览器标准(允许切换到SPA模式并控制工具栏导航)之前,我们不会获得接近完美的解决方案。
代码库
可以在Github上的CEC.Blazor.Editor上找到该代码。
控制SPA导航
第一个挑战——如何阻止用户访问标准页面上的任何按钮/链接导航内容?
- 建立一个自定义NavigationManager。这不是一个坏主意,但这是Blazor基础架构的核心部分,带来了许多后果。我说清楚了。
- 建立一个自定义Router。也不是一个坏主意。我已经做到了,在Nuget上有一个已发布的软件包,一个Github Repo和此站点上的一篇文章。我仍然想摆脱一些问题。
- 构建一个导航组件,在整个应用程序中使用它进行所有导航。同样不是一个坏主意,但是需要严格的纪律以在任何团队环境中执行。
- 使用“模态对话框”,锁定表单的其余部分。我的首选选项以及我将在此处进行探索和开发的选项。
注意——这里的大多数代码都是实验性的——绝对不适合生产。几乎没有错误检查等等。尝试保持透明性是民权主义者。
建立对话
首先,我们建立一个基本的模态对话框。这与CSS框架无关。欢迎您采用它以适合Bootstrap等。
ModalDialog1
将一个razor组件添加到“共享”中,并使用一个名为ModalDialog1的文件后面的代码。添加以下代码。它是天气预报的伪装编辑。
是的,这是Bootstrap!
@if (this.Display)
{
<div class="modal-background">
<div class="modal-content">
<div class="container-fluid">
<div class="row">
<div class="col">
Date
</div>
<div class="col">
@DateTime.Now.ToLongDateString();
</div>
</div>
<div class="row">
<div class="col">
Temperature C
</div>
<div class="col">
0 deg C
</div>
</div>
<div class="row">
<div class="col">
Temperature C
</div>
<div class="col">
32 deg F
</div>
</div>
<div class="row">
<div class="col">
Summary
</div>
<div class="col">
Another Beast-from-the-East day
</div>
</div>
<div class="row">
<div class="col-12 text-right">
<button class="btn btn-secondary"
@onclick="() => Hide()">Close</button>
</div>
</div>
</div>
</div>
</div>
}
using Microsoft.AspNetCore.Components;
namespace CEC.Blazor.Editor.Shared
{
public partial class ModalDialog1 : ComponentBase
{
public bool Display { get; private set; }
public void Show()
{
this.Display = true;
this.InvokeAsync(this.StateHasChanged);
}
public void Hide()
{
this.Display = false;
this.InvokeAsync(this.StateHasChanged);
}
}
}
笔记
- 我们用Show来控制模式的显示。无需JavaScript和CSS即可对其进行切换。
- 我们有两个public方法Show或Hide对话框。
- 我们调用StateHasChanged以渲染组件。没有外部触发器可以重新渲染组件(没有更改参数,也没有发生UI事件),因此我们需要强制进行渲染。评论他们,看看会发生什么!
FetchData1
我们使用FetchData页面组件作为模板,因此在其中创建一个新的Razor组件Pages并调用它FetchData1。在文件后面创建一个FetchData1.razor.cs代码。切记将类标记为partial。
添加以下代码:
@page "/fetchdata1"
@using CEC.Blazor.Editor.Data
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
<td><button class="btn btn-primary"
@onclick="() => ShowModalDialog()">Edit</button></td>
</tr>
}
</tbody>
</table>
}
<ModalDialog1 @ref="this.Modal"></ModalDialog1>
using CEC.Blazor.Editor.Data;
using CEC.Blazor.Editor.Shared;
using Microsoft.AspNetCore.Components;
using System;
using System.Threading.Tasks;
namespace CEC.Blazor.Editor.Pages
{
public partial class FetchData1 : ComponentBase
{
[Inject] WeatherForecastService ForecastService { get; set; }
private WeatherForecast[] forecasts;
private ModalDialog1 Modal { get; set; }
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
private void ShowModalDialog()
{
this.Modal.Show();
}
}
}
笔记
- 我们已在每行中添加了一个编辑按钮,以访问编辑器(链接到)ShowModalDialog。
- 我们已经在页面底部添加了一个ModalDialog1组件,并将@ref添加到属性中。
- 我们添加了一个ModalDialog1属性。
- 我们添加了一种ShowModalDialog方法来打开“模态对话框”。
在构建之前,我们需要做一些最后的更改。
NavMenu.razor
我们需要在NavMenu中添加一个指向新页面的链接。
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata1">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data 1
</NavLink>
</li>
Site.css
我们需要将“模态对话框”的css添加到站点css文件中。我们可以将此代码移动到组件css文件中进行生产,但在site.css中即时编辑更容易。
div.modal-background {
display: block;
position: fixed;
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
div.modal-content {
background-color: #fefefe;
margin: 10% auto;
padding: 10px;
border: 2px solid #888;
width: 90%;
}
运行版本1
运行该应用程序,然后转到FetchData1。点击一个编辑按钮。基础页面上的所有链接均被禁用。三种退出路径是:
- 该关闭按钮。
- 输入一个新的URL。
- 关闭浏览器选项卡或关闭浏览器。
我知道有第四,第五…(杀死进程,按电源开关…),但是没有什么可以阻止这些。
版本2
现在关闭这三个退出。我已经在仓库中创建了version2文件,但是您可以根据需要更新原始文件。
Site.js
将js文件夹添加到wwwroot,然后添加一个site.js文件。
添加以下代码。
- 我们定义了window.cecblazor_showExitDialog——一个事件函数,它会弹出浏览器“Are You Sure”实现。不同浏览器的外观有所不同,但总是会提出某种退出挑战。
- 我们定义了window.cecblazor_setEditorExitCheck,其通过Blazor的JsInterop调用的方法,以添加和删除事件处理程序。
window.cecblazor_setEditorExitCheck = function (show) {
if (show) {
window.addEventListener("beforeunload", cecblazor_showExitDialog);
}
else {
window.removeEventListener("beforeunload", cecblazor_showExitDialog);
}
}
window.cecblazor_showExitDialog = function (event) {
event.preventDefault();
event.returnValue = "There are unsaved changes on this page. Do you want to leave?";
}
我们需要将此js文件包含在我们的应用程序中。更新Host._cshtml。
.....
</div>
<script src="_framework/blazor.server.js"></script>
<script src="js/site.js"></script>
</body>
</html>
模态对话框
我们需要向模式对话框添加更多功能,以模拟脏表单。更新ModalDialog中的按钮行。
<div class="row">
<div class="col-12 text-right">
<button class="btn @this.DirtyButtonCss"
@onclick="() => SetDirty()">@this.DirtyButtonText</button>
@if (this.DirtyExit)
{
<button class="btn btn-danger" @onclick="() =>
DirtyHide()">Dirty Close</button>
<button class="btn btn-dark" @onclick="() =>
CancelHide()">Cancel</button>
}
else
{
<button class="btn btn-secondary"
@onclick="() => Hide()">Close</button>
}
</div>
</div>
更新文件添加背后的代码:
- 布尔属性——IsDirty、IsLocked以及DirtyExit——向控制如果控制状态和按钮显示。
- 注入IJSRuntime以访问JSInterop。
- CSS字符串属性来控制按钮CSS。
- 各种按钮事件处理程序,用于切换状态。您应该能够自己算出逻辑。
- SetPageExitCheck 与页面JS进行交互,并打开和关闭浏览器退出挑战。
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace CEC.Blazor.Editor.Shared
{
public partial class ModalDialog2 : ComponentBase
{
[Inject] private IJSRuntime _js { get; set; }
public bool Display { get; private set; }
public bool IsDirty { get; set; }
public bool IsLocked { get; private set; }
private bool DirtyExit { get; set; }
private string DirtyButtonCss => this.IsDirty ? "btn-danger" : "btn-success";
private string DirtyButtonText => this.IsDirty ? "Set Clean" : "Set Dirty";
public void Show()
{
this.Display = true;
this.InvokeAsync(this.StateHasChanged);
}
public void Hide()
{
if (this.IsDirty)
this.DirtyExit = true;
else
this.Display = false;
this.InvokeAsync(this.StateHasChanged);
}
public void DirtyHide()
{
this.Display = false;
this.DirtyExit = false;
if (this.IsDirty)
{
this.IsDirty = false;
CheckLock();
}
this.InvokeAsync(this.StateHasChanged);
}
public void CancelHide()
{
this.DirtyExit = false;
this.InvokeAsync(this.StateHasChanged);
}
public void SetDirty()
{
if (this.IsDirty)
this.DirtyExit = false;
this.IsDirty = !this.IsDirty;
this.CheckLock();
this.InvokeAsync(this.StateHasChanged);
}
public void SetPageExitCheck(bool action)
{
_js.InvokeAsync<bool>("cecblazor_setEditorExitCheck", action);
}
public void CheckLock()
{
if (this.IsDirty && !this.IsLocked)
{
this.IsLocked = true;
this.SetPageExitCheck(true);
}
else if (this.IsLocked && !this.IsDirty)
{
this.IsLocked = false;
this.SetPageExitCheck(false);
}
}
}
}
运行版本2
现在运行该应用程序。点击一个编辑按钮。基础页面上的所有链接均被禁用。单击设置污点以模拟编辑字段。现在尝试关闭或导航或关闭浏览器。所有选项都应包括在内
- 关闭浏览器,在工具栏中导航,F5或关闭浏览器窗口,将在浏览器中显示“您是否要离开对话框”。
- 关闭会为您提供“脏退出”选项。
- 单击浏览器窗口中的任何位置都不会执行任何操作。
版本3
现在让我们继续创建可以在生产中使用的ModalDialog框架。这将是一个模态对话框包装,我们可以使用它来显示任何组件。在这种情况下,WeatherViewer和ModalEditor组成。我创建了一个Component目录结构来组织代码,但是为了简单起见,将它们全部保留在基本名称空间中。
首先,我们需要添加一些实用程序类。
ModalResult和ModalResultType
在Components/ModalDialog中,添加一个ModalResult类。代码如下。
Dialog关闭时,该类为返回值提供了一个容器。它返回一个对象Data和一个ModalResultType。静态构造函数用于构建实例。
namespace CEC.Blazor.Editor
{
public enum ModalResultType { NoSet, OK, Cancel, Exit }
public class ModalResult
{
public ModalResultType ResultType { get; private set; } = ModalResultType.NoSet;
public object Data { get; set; } = null;
public static ModalResult OK() => new ModalResult()
{ ResultType = ModalResultType.OK };
public static ModalResult Exit() => new ModalResult()
{ ResultType = ModalResultType.Exit };
public static ModalResult Cancel() => new ModalResult()
{ ResultType = ModalResultType.Cancel };
public static ModalResult OK(object data) => new ModalResult()
{ Data = data, ResultType = ModalResultType.OK };
public static ModalResult Exit(object data) => new ModalResult()
{ Data = data, ResultType = ModalResultType.Exit };
public static ModalResult Cancel(object data) => new ModalResult()
{ Data = data, ResultType = ModalResultType.Cancel };
}
}
ModalOptions
现在添加一个ModalOptions类。它是将配置选项传递到模式对话框的容器。这是带有getter和setter的简单IEnumerable集合对象。
using System.Collections;
using System.Collections.Generic;
namespace CEC.Blazor.Editor
{
public class ModalOptions :IEnumerable<KeyValuePair<string, object>>
{
// Ststic List of options - only one currently defined
public static readonly string __Width = "Width";
private Dictionary<string, object> Parameters { get; } =
new Dictionary<string, object>();
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
foreach (var item in Parameters)
yield return item;
}
IEnumerator IEnumerable.GetEnumerator()
=> this.GetEnumerator();
public T Get<T>(string key)
{
if (this.Parameters.ContainsKey(key))
{
if (this.Parameters[key] is T t) return t;
}
return default;
}
public bool TryGet<T>(string key, out T value)
{
value = default;
if (this.Parameters.ContainsKey(key))
{
if (this.Parameters[key] is T t)
{
value = t;
return true;
}
}
return false;
}
public bool Set(string key, object value)
{
if (this.Parameters.ContainsKey(key))
{
this.Parameters[key] = value;
return false;
}
this.Parameters.Add(key, value);
return true;
}
}
}
IModal对话
添加IModalDialog接口。它定义了我们构建的任何模式对话框都需要实现的public属性和方法。它在我们的代码和特定的模态对话框实现之间提供了一个抽象层——干净的CSS版本,BootStrap版本…。
using Microsoft.AspNetCore.Components;
using System.Threading.Tasks;
namespace CEC.Blazor.Editor
{
public interface IModalDialog
{
public ModalOptions Options { get; }
Task<ModalResult> ShowAsync<TModal>(ModalOptions options) where TModal : IComponent;
void Dismiss();
void Close(ModalResult result);
void Update(ModalOptions options = null);
void Lock(bool setlock);
}
}
模态对话框
现在,添加ModalDialog,IModalDialog的基本CSS不可知实现。
Razor标记非常简单,类似于我们编写的原型:
- Display 将其关闭然后再打开。
- 该ModalDialog实例被级联,从而包裹部件具有穿过访问模态对话框的IModalDialog接口。
- 宽度可以通过ModalOptions设置。
- 内容在RenderFragment中构建,命名为_Content。
@namespace CEC.Blazor.Editor
@implements IModalDialog
@if (this.Display)
{
<CascadingValue Value="(IModalDialog)this">
<div class="modal-background">
<div class="modal-content" style="@this.Width">
@this._Content
</div>
</div>
</CascadingValue>
}
在代码中:
- 我们有一个通过ShowAsync传递的ModalOptions属性。
- 我们从ModalOptions得到Width。
- 我们使用一个TaskCompletionSource对象来提供异步行为,而Task调用者可以await。
- ShowAsync使用泛型。TModal可以是IComponent–ComponentBase实现的任何组件。
- ShowAsync_Content使用RenderTreeBuilder进行构建:将组件类型TModal的新实例添加到_Content中。然后将其Display设置为true并且模式对话框重新渲染。如果TModal为WeatherEditor,则控件有效地以<WeatherEditor></WeatherEditor>作为其内容。
- Dismiss和Close通过设置Display为false并呈现控件来关闭“模态”对话框。他们还清除内容并设置Task调用者可能正在等待完成的内容。
- 其余代码是原型代码的修改版本。
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Threading.Tasks;
namespace CEC.Blazor.Editor
{
public partial class ModalDialog : IModalDialog
{
[Inject] private IJSRuntime _js { get; set; }
public ModalOptions Options { get; protected set; } = new ModalOptions();
public bool Display { get; protected set; }
public bool IsLocked { get; protected set; }
protected RenderFragment _Content { get; set; }
protected string Width => this.Options.TryGet<string>
(ModalOptions.__Width, out string value) ? $"width:{value}" : string.Empty;
protected TaskCompletionSource<ModalResult>
_ModalTask { get; set; } = new TaskCompletionSource<ModalResult>();
public Task<ModalResult> ShowAsync<TModal>(ModalOptions options)
where TModal : IComponent
{
this.Options = options ??= this.Options;
this._ModalTask = new TaskCompletionSource<ModalResult>();
this._Content = new RenderFragment(builder =>
{
builder.OpenComponent(1, typeof(TModal));
builder.CloseComponent();
});
this.Display = true;
InvokeAsync(StateHasChanged);
return this._ModalTask.Task;
}
/// <summary>
/// Method to update the state of the display based on UIOptions
/// </summary>
/// <param name="options"></param>
public void Update(ModalOptions options = null)
{
this.Options = options ??= this.Options;
InvokeAsync(StateHasChanged);
}
/// <summary>
/// Method called by the dismiss button to close the dialog
/// sets the task to complete, show to false and renders the component
/// (which hides it as show is false!)
/// </summary>
public async void Dismiss()
{
_ = this._ModalTask.TrySetResult(ModalResult.Cancel());
this.Display = false;
this._Content = null;
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Method called by child components through the cascade value of this component
/// sets the task to complete, show to false and renders the component
/// (which hides it as show is false!)
/// </summary>
/// <param name="result"></param>
public async void Close(ModalResult result)
{
_ = this._ModalTask.TrySetResult(result);
this.Display = false;
this._Content = null;
await InvokeAsync(StateHasChanged);
}
private void SetPageExitCheck(bool action)
{
_js.InvokeAsync<bool>("cecblazor_setEditorExitCheck", action);
}
public void Lock(bool setlock)
{
if (setlock && !this.IsLocked)
{
this.IsLocked = true;
this.SetPageExitCheck(true);
}
else if (this.IsLocked && !setlock)
{
this.IsLocked = false;
this.SetPageExitCheck(false);
}
}
}
}
天气组件
我只在这里展示WeatherEditor。WeatherViewer是没有“脏”功能的较简单版本。有关代码,请参见Repo。
天气编辑器
Razor代码与原型模式对话框中的代码相同。
@namespace CEC.Blazor.Editor
<div class="container-fluid">
<div class="row">
<div class="col">
<h2>Weather Editor</h2>
</div>
</div>
<div class="row">
<div class="col">
Date
</div>
<div class="col">
@DateTime.Now.ToLongDateString()
</div>
</div>
<div class="row">
<div class="col">
Temperature C
</div>
<div class="col">
0 deg C
</div>
</div>
<div class="row">
<div class="col">
Temperature C
</div>
<div class="col">
32 deg F
</div>
</div>
<div class="row">
<div class="col">
Summary
</div>
<div class="col">
Another Beast-from-the-East day
</div>
</div>
<div class="row">
<div class="col-12 text-right">
<button class="btn @this.DirtyButtonCss mr-1"
@onclick="() => SetDirty()">@this.DirtyButtonText</button>
@if (this.DoDirtyExit)
{
<button class="btn btn-danger mr-1"
@onclick="() => DirtyExit()">Dirty Close</button>
<button class="btn btn-dark mr-1"
@onclick="() => CancelExit()">Cancel</button>
}
else
{
<button class="btn btn-secondary mr-1"
@onclick="() => Exit()">Close</button>
}
</div>
</div>
</div>
许多控制代码也从原型代码中提取。我们选择级联IModalDialog以使用其public接口方法与ModalDialog包装器进行交互。例如, Exit检查控件是否IsDirty。如果很干净,它会调用this.Modal?.Close(ModalResult.OK()),以关闭对话框包装并销毁此类的实例。
using Microsoft.AspNetCore.Components;
namespace CEC.Blazor.Editor
{
public partial class WeatherEditor : ComponentBase
{
[CascadingParameter] private IModalDialog Modal { get; set; }
public bool IsDirty { get; set; }
public bool IsLocked { get; private set; }
private bool DoDirtyExit { get; set; }
private string DirtyButtonCss => this.IsDirty ? "btn-warning" : "btn-info";
private string DirtyButtonText => this.IsDirty ? "Set Clean" : "Set Dirty";
private void Exit()
{
if (this.IsDirty)
{
this.DoDirtyExit = true;
this.InvokeAsync(this.StateHasChanged);
}
else
this.Modal?.Close(ModalResult.OK());
}
public void DirtyExit()
{
if (this.DoDirtyExit)
{
this.IsDirty = false;
this.Modal?.Lock(false);
this.Modal?.Close(ModalResult.Cancel());
}
}
public void CancelExit()
{
this.DoDirtyExit = false;
this.InvokeAsync(this.StateHasChanged);
}
public void SetDirty()
{
if (this.IsDirty)
this.DoDirtyExit = false;
this.IsDirty = !this.IsDirty;
this.Modal?.Lock(this.IsDirty);
this.InvokeAsync(this.StateHasChanged);
}
}
}
FetchDataModal
最后,我们在Pages中创建一个被称为FetchDataModal的FetchData新版本。
Razor标记与原型非常相似。
- 我们正在使用ModalDialog模式对话框的版本。
- 现在,每行都有两个按钮以显示“编辑器”或“查看器”。
@page "/fetchdatamodal"
@using CEC.Blazor.Editor.Data
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
<td><button class="btn btn-secondary"
@onclick="() => ShowViewDialog()">View</button></td>
<td><button class="btn btn-primary"
@onclick="() => ShowEditDialog()">Edit</button></td>
</tr>
}
</tbody>
</table>
}
<ModalDialog @ref="this.Modal"></ModalDialog>
与原型非常相似,但:
- ShowViewDialog设置ModalOptions然后调用await this.Modal.ShowAsync<WeatherViewer>(options)。我们正在运行的异步和完成之前等待的对话框打开,然后关闭,并传递一个类型WeatherViewer的Modaldialog以显示。
- ShowEditDialog如上操作,但加载WeatherViewer。
using CEC.Blazor.Editor.Data;
using Microsoft.AspNetCore.Components;
using System;
using System.Threading.Tasks;
namespace CEC.Blazor.Editor.Pages
{
public partial class FetchDataModal : ComponentBase
{
[Inject] WeatherForecastService ForecastService { get; set; }
private WeatherForecast[] forecasts;
private ModalDialog Modal { get; set; }
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
private async void ShowViewDialog()
{
var options = new ModalOptions();
options.Set(ModalOptions.__Width, "60%");
await this.Modal.ShowAsync<WeatherViewer>(options);
}
private async void ShowEditDialog()
{
var options = new ModalOptions();
options.Set(ModalOptions.__Width, "80%");
await this.Modal.ShowAsync<WeatherEditor>(options);
// any code to execute after the editor is complete goes here
}
}
}
总结
希望我已经展示了一些用于处理Blazor和SPA中的编辑状态的技术和策略。本文的一些要点:
- 您可以从肮脏的表单中阻止用户拥有的所有正常退出路线。
- 您可以构建HTML对话框,其行为与桌面应用程序中的模式对话框相似。
- 模式对话框可以是异步的,您可以等待其完成。
https://www.codeproject.com/Articles/5294466/A-Blazor-Modal-Dialog-Editor
今天的文章Blazor Modal对话框编辑器分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/4972.html