Blazor 内联对话框控件






4.43/5 (3投票s)
一个 Blazor 内联对话框控件,用于锁定页面上的所有控件,除了表单内的控件。
内联对话框控件
这是本系列文章的第三篇,介绍了 Blazor 的一系列有用的编辑控件,它们解决了开箱即用的编辑体验中存在的一些不足之处,而无需购买昂贵的工具包。
本文介绍如何构建一个组件,该组件可以禁用链接、按钮、URL 栏……:除了组件内的内容之外的所有内容。虽然它无法阻止用户通过浏览器控件进行导航,但它会开启浏览器的 beforeunload
事件,以强制弹出“您确定要离开此网站吗?”对话框。所有这些都通过一个相对简单的标准 Blazor 组件和一个小型 js
文件实现。
链接
此存储库包含一个项目,其中实现了本系列所有文章的控件。您可以在此处找到它。
示例网站位于https://cec-blazor-database.azurewebsites.net/。
本文末尾描述的示例表单可以在 https://cec-blazor-database.azurewebsites.net//inlinedialogeditor 找到。
之前的文章
概述
如果您想查看该组件的实际运行效果,请访问我演示站点的 此页面。这是一个基础模型,用于演示功能,并扩展了前两篇文章中使用的表单。有一个典型的编辑表单,包含前两篇文章介绍的两个额外控件
EditFormState
监控Model
数据的编辑状态ValidationFormState
- 表单验证器
关键的动作是连接 InlineDialog
控件的 Lock
到表单状态。 EditFormState
监控表单状态,并在每次发生更改时调用 EventCallback EditStateChanged
。页面 EditStateChanged
事件处理程序已注册到 EditFormState.EditStateChanged
,并在状态更改时更新 _isDirty
。如果 EditFormState' 脏了,则
InlineDialog
会被锁定。
@using Blazor.Database.Data
@page "/inlinedialogEditor"
<InlineDialog Lock="this._isDirty" Transparent="false">
<EditForm Model="@Model" OnValidSubmit="@HandleValidSubmit" class="p-3">
<EditFormState @ref="editFormState" EditStateChanged="this.EditStateChanged">
</EditFormState>
<ValidationFormState @ref="validationFormState"></ValidationFormState>
<label class="form-label">ID:</label>
<InputNumber class="form-control" @bind-Value="Model.ID" />
<label class="form-label">Date:</label>
<InputDate class="form-control" @bind-Value="Model.Date" />
<ValidationMessage For="@(() => Model.Date)" />
<label class="form-label">Temp C:</label>
<InputNumber class="form-control" @bind-Value="Model.TemperatureC" />
<ValidationMessage For="@(() => Model.TemperatureC)" />
<label class="form-label">Summary:</label>
<InputText class="form-control" @bind-Value="Model.Summary" />
<ValidationMessage For="@(() => Model.Summary)" />
<div class="mt-2">
<div>Validation Messages:</div>
<ValidationSummary />
</div>
<div class="text-right mt-2">
<button class="btn @btnStateColour" disabled>@btnStateText</button>
<button class="btn @btnValidColour" disabled>@btnValidText</button>
<button class="btn btn-primary" type="submit"
disabled="@_btnSubmitDisabled">Submit</button>
</div>
</EditForm>
</InlineDialog>
}
@code {
protected bool _isDirty = false;
protected bool _isValid => validationFormState?.IsValid ?? true;
protected string btnStateColour => _isDirty ? "btn-danger" : "btn-success";
protected string btnStateText => _isDirty ? "Dirty" : "Clean";
protected string btnValidColour => !_isValid ? "btn-danger" : "btn-success";
protected string btnValidText => !_isValid ? "Invalid" : "Valid";
protected bool _btnSubmitDisabled => !(_isValid && _isDirty);
protected EditFormState editFormState { get; set; }
protected ValidationFormState validationFormState { get; set; }
private WeatherForecast Model = new WeatherForecast()
{
ID = 1,
Date = DateTime.Now,
TemperatureC = 22,
Summary = "Balmy"
};
private void HandleValidSubmit()
{
this.editFormState.UpdateState();
}
private void EditStateChanged(bool editstate)
=> this._isDirty = editstate;
}
内联对话框
让我们先看看参数和 public
属性。
- 我们捕获添加的属性,尽管我们只使用
class
。 Cascade
打开/关闭this
的参数级联,即InlineDialog
的实例。默认值为true
。Transparent
将背景设置为透明或半透明。演示设置为半透明,以便您可以看到它的切换。ChildContent
是<InlineDialog>
和</InlineDialog>
之间的内容。IsLocked
是一个只读属性,用于检查组件状态。
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>
AdditionalAttributes { get; set; } = new Dictionary<string, object>();
[Parameter] public bool Cascade { get; set; } = true;
[Parameter] public bool Transparent { get; set; } = true;
[Parameter] public RenderFragment ChildContent { get; set; }
public bool IsLocked => this._isLocked;
private
属性
- 注入
IJSRuntime
以访问 JavaScript Interop 并设置/取消设置浏览器的BeforeUnload
事件。 CssClass
构建组件的 HTMLclass
属性,将输入的任何类与组件构建的类相结合。- CSS 属性定义了
class
的各种 CSS 选项。 _isLocked
是用于控制锁定状态的private
字段。
[Inject] private IJSRuntime _js { get; set; }
private string CssClass => (AdditionalAttributes != null &&
AdditionalAttributes.TryGetValue("class", out var obj))
? $"{this.frontcss}
{ Convert.ToString(obj, CultureInfo.InvariantCulture)}"
: this.frontcss;
private string backcss = string.Empty;
private string frontcss = string.Empty;
private string _backcss => this.Transparent ? "back-block-transparent" : "back-block";
private string _frontcss => this.Transparent ? "fore-block-transparent" : "fore-block";
private string __backcss => string.Empty;
private string __frontcss => string.Empty;
private bool _isLocked;
有两个 public
方法:Lock
和 Unlock
。它们更改 CSS 类。 SetPageExitCheck
与 JavaScript 函数进行交互,以添加或删除 Window
上的 beforeunload
事件。代码如下所示
public void Lock()
{
this._isLocked = true;
this.backcss = this._backcss;
this.frontcss = this._frontcss;
this.SetPageExitCheck(true);
this.InvokeAsync(StateHasChanged);
}
public void Unlock()
{
this._isLocked = false;
this.backcss = this.__backcss;
this.frontcss = this.__frontcss;
this.SetPageExitCheck(false);
this.InvokeAsync(StateHasChanged);
}
private void SetPageExitCheck(bool action)
=> _js.InvokeAsync<bool>("cecblazor_setEditorExitCheck", action);
site.js 中的 JavaScript
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?";
}
继续组件的 Razor
- 我们添加一个带有 CSS 类
_backcss
的div
:当Locked
时,它为 back-block-transparent 或 back-block,当Unlocked
时为空。 - 我们添加一个带有 Css 类
_frontcss
的div
:当Locked
时,它为 fore-block-transparent 或 fore-block,当Unlocked
时为空,并结合我们添加到组件的任何class
属性值。 - 如果
Cascade
为true
,我们级联this
。
<div class="@this.backcss"></div>
<div class="@this.CssClass">
@if (this.Cascade)
{
<CascadingValue Value="this">
@this.ChildContent
</CascadingValue>
}
else
{
@this.ChildContent
}
</div>
继续组件的 CSS,魔法就发生在这里。我们实现了与模态对话框中使用的相似的 CSS 技术,在页面内容之上添加一个透明或半透明的层来 block
该层下方的内容,并将 InlineDialog
的内容放在该层的前面。如果您在应用程序中使用了大量 z-index 层,您可能需要调整 Z-index 以确保它置于最顶层。
div.back-block {
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: RGBA(224, 224, 224, 0.4);/* the translucent effect*/
}
div.back-block-transparent {
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: transparent;
}
div.fore-block-transparent {
display: block;
position: relative;
z-index: 2; /* Sit on top */
}
div.fore-block {
display: block;
position: relative;
z-index: 2; /* Sit on top */
background-color: RGB(255, 255, 255);/* need to set the colour, adjust as necessary */
}
总结
此解决方案使用了与模态对话框相同的技术,在页面控件和控件内容之间放置了一个屏障。它是一个就地的模态对话框。 Lock
插入屏障,Unlock
移除它。我们添加了 JavaScript Interop 来打开和关闭浏览器上的 beforeunload
事件。选择透明或半透明层,或者编写自己的 CSS。
在开发了许多解决此问题的方案并撰写了相关文章后,我终于找到了一个如此简单的解决方案,这让我有点不知所措。最好的解决方案总是最简单的!
如果您在很久以后读到这篇文章,最新版本的文章将在此处:here。
历史
- 2021年3月17日:初始版本