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

Time:04-16

I'm new to a Blazor and now I'm working to Blazor WebAssembly project. I have couple razor page with a table where I'm displaying data from SQL. When I click in one of the table rows it opens a page where I can do the CRUD operation.

Now, instead of opening a page to do the CRUD operation I need to open a bootstarp modal and do the CRUD operation. I'm doing a generic ModalComponent where I have the header and the footer of the modal. Is it possibile to load dynamically the body of the model, which in this case it will be the CRUD operation pages that I already had done?

CodePudding user response:

How to dynamically update Bootstrap modal content

Dynamically updating the content of a Bootstrap modal is easy. You add a property to store data which you update at the end of your database query. That property is used to build a table within the modal. The modal updates whenever the data updates.

I've created a Blazor Fiddle demo so that you can understand how it works. If you have long running queries then you might need some additional code to make run asynchronously, but that's beyond the scope of the question.

Add a property to store your data:

@code {

   List<Report> Reports {get; set; }= new List<Report>();

    // Simulate DB Query

    void QueryDatabase() {

        Reports = new List<Report> {
             new Report { Id = 1, Issued = DateTime.Now, Name = "Report 1", Description = "Annual Report" },
             new Report { Id = 2, Issued = DateTime.Now, Name = "Report 2", Description = "Semiannual Report" },                
             new Report { Id = 3, Issued = DateTime.Now, Name = "Report 3", Description = "Quarterly Report" },                
             new Report { Id = 4, Issued = DateTime.Now, Name = "Report 4", Description = "Status Report" },                
             new Report { Id = 5, Issued = DateTime.Now, Name = "Report 5", Description = "Summary Report" }                
        };    
    }
}

And then add some Razor markup to your modal-body to display the results:

  <div >
      <button  @onclick="QueryDatabase">Query Database</button>
      <table >
          <tbody>
       @foreach(var item in Reports) {
          <tr>
              <td>@item.Id</td>
              <td>@item.Issued.ToString("d")</td>
              <td>@item.Name</td>
              <td>@item.Description</td>
            </tr>
       }
       </tbody>
      </table>
  </div>

CodePudding user response:

This is not the first time this question has been asked.

The code below demonstrates how to implement a generic modal dialog i.e. a wrapper for any component/page that you want to display in modal mode. The concrete implementation of IModalDialog shows how to imnplement a Bootstrap version.

I use a more complex version of this. My edit/View components are written to run in modal or full page mode.

Support Classes

First three support classes that are fairly 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

Now the modal dialog 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);
}

The Base Bootstrap Implementation

  1. There's no JS.
  2. I use a TaskCompletionSource to make the open/close an async process.
  3. The component to display is passed as part of the open call.
@inherits ComponentBase
@implements IModalDialog

@if (this.Display)
{
    <CascadingValue Value="(IModalDialog)this">
        <div  tabindex="-1" style="display:block;">
            <div >
                <div >
                    <div >
                        <h5 >Modal title</h5>
                        <button type="button"  data-bs-dismiss="modal" aria-label="Close" @onclick="() => Close(ModalResult.Exit())"></button>
                    </div>
                    <div >
                        @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);
    }
}

Demo

A simple "Edit" component:

@inject NavigationManager NavManager

<h3>EditForm</h3>
<div >
    <button  @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("/");
    }
}

And a test page:

@page "/"

<div >
    <button  @onclick=OpenDialog>Click</button>
</div>

<div >
    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();
    }
}
  • Related