Blazor使用(一)封装Bootstrap5的模态框实现互操作

一、引入JS

要实现Bootstrap组件的功能,需要引用Bootstrap.js,依赖@popperjs/core

官方给出的方案

<body>
    <h1>Hello, modularity!</h1>
    <button id="popoverButton" type="button" class="btn btn-primary btn-lg" class="btn btn-lg btn-danger" data-bs-toggle="popover" title="ESM in Browser" data-bs-content="Bang!">Custom popover</button>

    <script async src="https://cdn.jsdelivr.net/npm/es-module-shims@1/dist/es-module-shims.min.js" crossorigin="anonymous"></script>
    <script type="importmap">
    {
      "imports": {
        "@popperjs/core": "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js",
        "bootstrap": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.esm.min.js"
      }
    }
    </script>
    <script type="module">
      import * as bootstrap from 'bootstrap'

      new bootstrap.Popover(document.getElementById('popoverButton'))
    </script>
</body>

但是无法加载popperjs和bootstrap.js,可以将这两个文件放在wwwroot再通过相对路径引用。注意“@”的转义

改为如下:

    <script async src="https://cdn.jsdelivr.net/npm/es-module-shims@1/dist/es-module-shims.min.js" crossorigin="anonymous"></script>
    <script type="importmap">
    {
        "imports": {
            "@("@popperjs/core")": "/js/popperjs/popper.min.js",
            "bootstrap": "/js/bootstrap/bootstrap.esm.min.js"      
        }
    }
    </script>
    <script type="module">
      import * as bootstrap from 'bootstrap'
    </script>

 

二、实现组件模板

MyModal.razor

<div class="modal fade" 
     id="@Id"
     @ref="ModalRef" 
     tabindex="-1" 
     aria-labelledby="modalLabel" 
     aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                @if(ModalHeader == null){
                    <h1 class="modal-title fs-5" 
                        id="modalLabel">
                        @(Title ?? "Modal Title")
                    </h1>
                }
                else
                {
                    @ModalHeader
                }
            </div>
            <div class="modal-body">
                @if(ModalBody != null)
                {
                    @ModalBody
                }
            </div>
            <div class="modal-footer">
                @if(ModalFooter == null)
                {
                    <button type="button"
                            class="btn btn-secondary" data-bs-dismiss="modal">
                        Close
                    </button>
                    <button type="button"
                            class="btn btn-primary">
                        Save Changes
                    </button>
                }
                else{
                    @ModalFooter
                }
            </div>
        </div>
    </div>    
</div>

@code {
    private ElementReference ModalRef { get; set; }

    [Parameter]
    public string? Id { get; set; }

    [Parameter]
    public string Title { get; set; } = "Modal Title";

    [Parameter]
    public RenderFragment? ModalHeader { get; set; }

    [Parameter]
    public RenderFragment? ModalBody { get; set; }

    [Parameter]
    public RenderFragment? ModalFooter { get; set; } 

    [Parameter]
    public EventCallback<EventArgs> OnHide { get; set; }

    [Parameter]
    public EventCallback<EventArgs> OnHidden { get; set; }
    
    [Parameter]
    public EventCallback<EventArgs> OnHidePrevented { get; set; }

    [Parameter]
    public EventCallback<EventArgs> OnShow { get; set; }

    [Parameter]
    public EventCallback<EventArgs> OnShown { get; set; }

    private IJSObjectReference? _MyModalModule { get; set; }
    
    [JSInvokable]
    public Task HandleEvent(string eventName)
    {
        return eventName switch {
            "show" =>  OnShow.InvokeAsync(new EventArgs()),
            "shown" =>  OnShown.InvokeAsync(new EventArgs()),
            "hide" =>  OnHide.InvokeAsync(new EventArgs()),
            "hidden" =>  OnHidden.InvokeAsync(new EventArgs()),
            "hidePrevented" =>  OnHidePrevented.InvokeAsync(new EventArgs()),
            _ =>  throw new NotSupportedException()
        };
    }

    [Inject]
    private IJSRuntime? _JS { get; set; }

    private IJSObjectReference? _JSModalObject { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if(firstRender)
        {
            _MyModalModule = await _JS!.InvokeAsync<IJSObjectReference>(
                "import", "./Components/MyModal.razor.js");
            
            var instance = DotNetObjectReference.Create<MyModal>(this);

            @* await _MyModalModule.InvokeVoidAsync("registryEvent", ModalRef, instance); *@

            _JSModalObject = await _MyModalModule.InvokeAsync<IJSObjectReference>("registryEvent", ModalRef, instance);
        }

        await base.OnAfterRenderAsync(firstRender);
    }

    public async Task Show() 
    {
        await _MyModalModule!.InvokeVoidAsync("showModal", _JSModalObject);
    }

    public async Task Hide()
    {
        await _MyModalModule!.InvokeVoidAsync("hideModal", _JSModalObject);
    }

    public async ValueTask DisposeAsync()
    {
        if(_MyModalModule != null)
        {
            await _MyModalModule.DisposeAsync();
        }
    }
}

MyModal.razor.js

三、对接Modal的事件

在Razor组件渲染完成后,调用JS注册Modal的事件,并且在JS中触发Razor组件的事件。

使用DotNetObjectReference创建Razor组件实例的引用,传递到JS。

MyModal.razor

private IJSObjectReference? _MyModalModule { get; set; }

[Inject]
private IJSRuntime? _JS { get; set; }
private IJSObjectReference? _JSModalObject { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
  if(firstRender)
  {
    // 加载组件JS     _MyModalModule
= await _JS!.InvokeAsync<IJSObjectReference>(       "import", "./Components/MyModal.razor.js");
// 构建要传递给JS的.NET对象引用
    var instance = DotNetObjectReference.Create<MyModal>(this);
    // 调用JS方法,返回Modal的JS对象的引用     _JSModalObject
= await _MyModalModule.InvokeAsync<IJSObjectReference>("registryEvent", ModalRef, instance);   }   await base.OnAfterRenderAsync(firstRender); }

调用JS注册事件,将组件的HTML引用传递给JS。同时也可以将构建的modal对象传递回Razor组件。

MyModal.razor.js

// import bootstrap
import * as bootstrap from 'bootstrap' let modal; /** * 注册事件 * @param {HTMLElement} el */ export function registryEvent(el, dotnetInstance) { modal = new bootstrap.Modal(el);  // BootStrap的构造方法 el.addEventListener("hide.bs.modal", (e) => {  // 在HTML元素上监听事件 handleBsModalHide(e, dotnetInstance); }) el.addEventListener("hidden.bs.modal", (e) => { handleBsModalHidden(e, dotnetInstance); }) el.addEventListener("hidePrevented.bs.modal", (e) => { handleBsModalHidePrevented(e, dotnetInstance); }) el.addEventListener("show.bs.modal", (e) => { handleBsModalShow(e, dotnetInstance); }) el.addEventListener("shown.bs.modal", (e) => { handleBsModalShown(e, dotnetInstance); }) return modal;   // 返回.NET }

const handleBsModalShow = (e, i) => { i.invokeMethodAsync("HandleEvent", "show") } const handleBsModalShown = (e, i) => { i.invokeMethodAsync("HandleEvent", "shown") } const handleBsModalHide = (e, i) => { i.invokeMethodAsync("HandleEvent", "hide") } const handleBsModalHidden = (e, i) => { i.invokeMethodAsync("HandleEvent", "hidden") } const handleBsModalHidePrevented = (e, i) => { i.invokeMethodAsync("HandleEvent", "hidePrevented") }

事件处理

 
[Parameter]
public EventCallback<EventArgs> OnHide { get; set; }
 
[Parameter]
public EventCallback<EventArgs> OnHidden { get; set; }
 
[Parameter]
public EventCallback<EventArgs> OnHidePrevented { get; set; }
 
[Parameter]
public EventCallback<EventArgs> OnShow { get; set; }
 
[Parameter]
public EventCallback<EventArgs> OnShown { get; set; }
 
// 时间参数也可以自定义
[JSInvokable]
public Task HandleEvent(string eventName)
{
    return eventName switch {
            "show" =>  OnShow.InvokeAsync(new EventArgs()),
            "shown" =>  OnShown.InvokeAsync(new EventArgs()),
            "hide" =>  OnHide.InvokeAsync(new EventArgs()),
            "hidden" =>  OnHidden.InvokeAsync(new EventArgs()),
            "hidePrevented" =>  OnHidePrevented.InvokeAsync(new EventArgs()),
            _ =>  throw new NotSupportedException()
};
}

Razor组件上实现监听

<MyModal Id="@modalId" @ref="modalRef"
         OnHidden="@(() => Clear())">
</MyModal>

四、从.NET调用Modal的JS方法

封装Modal的hide(),show()方法,传入Modal的实例,实现操作。

MyModal.razor.js

export function showModal(component) {
    component.show();
}

export function hideModal(component) {
    component.hide();
}

根据在上面拿到的返回的Modal的实例的引用,在.NET中进行调用

MyModal.razor

public async Task Show() 
{
  await _MyModalModule!.InvokeVoidAsync("showModal", _JSModalObject);
}

public async Task Hide()
{
  await _MyModalModule!.InvokeVoidAsync("hideModal", _JSModalObject);
}

使用

<MyModal Id="@modalId" @ref="modalRef"
         OnHidden="@(() => Clear())">
    <ModalFooter>
        <button type="button" @onclick="@(async () => await OnSave())"
        class="btn btn-primary">
            保存
        </button>
    </ModalFooter>
</MyModal>

@code {
  public const string modalId = "settingModal";
  private MyModal? modalRef { get; set; }
  private async Task OnSave()   
  {
    if(modalRef != null){       await modalRef.Hide();      
    }   
  } }

 

posted @ 2023-02-10 06:00  Watt不想上班  阅读(501)  评论(0编辑  收藏  举报