是否可以在 Bootstrap 模态主体中动态加载现有页面

Is it possible to load dynamically existing page in a Bootstrap Modal body

我是 Blazor 的新手,现在我正在从事 Blazor WebAssembly 项目。 我有几个带有 table 的剃须刀页面,我在其中显示来自 SQL 的数据。当我单击 table 行之一时,它会打开一个页面,我可以在其中进行 CRUD 操作。

现在,我不需要打开一个页面来执行 CRUD 操作,而是需要打开一个 bootstarp 模式并执行 CRUD 操作。 我正在做一个通用的 ModalComponent,其中我有模态的页眉和页脚。 是否可以动态加载模型的主体,在这种情况下它将是我已经完成的 CRUD 操作页面?

下面的代码演示了如何实现通用模态对话框,即要在模态模式下显示的任何 component/page 的包装器。 IModalDialog的具体实现展示了如何实现一个Bootstrap版本。

我使用了一个更复杂的版本。我的 edit/View 组件以模态或整页模式写入 运行。

支持类

前三个支持类相当self-evident:

public class ModalResult
{
    public ModalResultType ResultType { get; private set; } = ModalResultType.NoSet;

    // Whatever object you wish to pass back
    public object? Data { get; set; } = null;

    // A set of static methods to build a BootstrapModalResult
    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 };
}
public class ModalOptions
{
    // Whatever data you want to pass
    // my complex version uses a <string, object> dictionary
}

public enum ModalResultType
{
    NoSet,
    OK,
    Cancel,
    Exit
}

IModalDialog 界面

现在模式对话框Interface

using Microsoft.AspNetCore.Components;

public interface IModalDialog
{
    ModalOptions Options { get; }

    //  Method to display a Modal Dialog
    Task<ModalResult> ShowAsync<TModal>(ModalOptions options) where TModal : IComponent;

    // Method to update the Modal Dialog during display
    void Update(ModalOptions? options = null);

    // Method to dismiss - normally called by the dismiss button in the header bar
    void Dismiss();

    // Method to close the dialog - normally called by the child component TModal
    void Close(ModalResult result);
}

基础Bootstrap实施

  1. 没有JS。
  2. 我使用 TaskCompletionSource 使 open/close 成为一个异步进程。
  3. 要显示的组件作为打开调用的一部分传递。
@inherits ComponentBase
@implements IModalDialog

@if (this.Display)
{
    <CascadingValue Value="(IModalDialog)this">
        <div class="modal" tabindex="-1" style="display:block;">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">Modal title</h5>
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => Close(ModalResult.Exit())"></button>
                    </div>
                    <div class="modal-body">
                        @this.Content
                    </div>
                </div>
            </div>
        </div>
    </CascadingValue>
}
@code {
    public ModalOptions Options { get; protected set; } = new ModalOptions();
    public bool Display { get; protected set; }
    protected RenderFragment? Content { get; set; }

    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;
    }

    public void Update(ModalOptions? options = null)
    {
        this.Options = options ??= this.Options;
        InvokeAsync(StateHasChanged);
    }

    public async void Dismiss()
    {
        _ = this._ModalTask.TrySetResult(ModalResult.Cancel());
        this.Display = false;
        this.Content = null;
        await InvokeAsync(StateHasChanged);
    }

    public async void Close(ModalResult result)
    {
        _ = this._ModalTask.TrySetResult(result);
        this.Display = false;
        this.Content = null;
        await InvokeAsync(StateHasChanged);
    }
}

演示

一个简单的“编辑”组件:

@inject NavigationManager NavManager

<h3>EditForm</h3>
<div class="p-3">
    <button class="btn btn-success" @onclick=Close>Close</button>
</div>

@code {
    [CascadingParameter] private IModalDialog? modal { get; set; }

    private void Close()
    {
        if (modal is not null)
            modal.Close(ModalResult.OK());
        else
            this.NavManager.NavigateTo("/");
    }
}

还有一个测试页:

@page "/"

<div class="p-2">
    <button class="btn btn-primary" @onclick=OpenDialog>Click</button>
</div>

<div class="p-2">
    result: @Value
</div>

<ModalDialog @ref=this.modal />

@code {
    private string Value { get; set; } = "Fred";

    private IModalDialog? modal;
    private IModalDialog Modal => modal!;

    private async void OpenDialog()
    {
        var options = new ModalOptions();
        var ret = await Modal.ShowAsync<EditorForm>(new ModalOptions());
        if (ret is not null)
        {
            Value = ret.ResultType.ToString();
        }
        StateHasChanged();
    }
}