ASP.NET Core 3.1 - Message Modal


Ken Haggerty
Created 05/08/2020 - Updated 06/30/2020 16:27

This article will demonstrate the implementation of a global Bootstrap message modal. I will assume you have created a new ASP.NET Core 3.1 Razor Pages project and have reviewed the previous articles of the series. I won't use Identity or Individual User Accounts. See Tutorial: Get started with Razor Pages in ASP.NET Core.

FIDO Utilties Project and Article Series


Access to the research project source code may be purchased on KenHaggerty.Com at Manage > Assets.

I will publish the FIDO Utilities Project at fido.kenhaggerty.com until I publish the Users Without Passwords Project.

I enjoy writing these articles. It often enhances and clarifies my coding. The research project is a result of a lot of refactoring and hopefully provides logical segues for the articles. Thank you for supporting my efforts.

The FIDO processes involve interaction between the client, authenticator and user without the server. I found the frequency of JS alerts required better messaging. A message to inform the user something went wrong and they need to start over is the most important. This oops message needs to prevent the user from continuing on the current page and provide a link to the start over page. My extensive research found Oops means you made a mistake and realize it now. Opps means you made a mistake and wish to try for the bonus round.

The first oops message in the Users Without Passwords Project was a Bootstrap-jQuery modal with no dismiss button, a link to start over, a 'static' backdrop and keyboard=false. I liked it enough to develop a success message to present before automatically navigating to the goal. I liked that enough to develop a global message modal by adding the html for a modal to _Layout. cshtml and a showMessageModal() function to site.js. I liked that enough to develop message-modal.js which dynamically creates the html. Variables reference the created modal components which allow the message modal to co-exisit with other modals. It can be loaded in _Layout. cshtml for global access or used locally by loading from the page. The showMessageModal() function has defaults and parameters.

The first messages used a lot of jQuery. I converted the selectors and attribute modifications to vanilla JavaScript but the modal instance requires jQuery or Bootstrap Native. KenHaggerty.Com implements Bootstrap Native and ASP.NET Core 2.2 - BootstrapNative Client Validation. The FIDO Utilities Project implements libman.json and includes Bootstrap Native, jQuery and jQuery validation. I implemented a global UseBSN flag and added Bootstrap, Bootstrap Native and jQuery CDN links with integrity metadata in _Layout. cshtml. See more on libman.json at ASP.NET Core 2.2 - Manage Client-Side Libraries.

jQuery's Total Size = 170,560 bytes
  • jquery.min.js v3.5.1 = 89,476 bytes
  • bootstrap.bundle.min.js v4.5.0 = 81,084 bytes
Bootstrap Native's Total Size = 30,184‬ bytes
  • bootstrap-native.min.js v3.0.5 = 28,089 bytes
  • pollyfill.min.js = 2,095 bytes

The message-modal.js depends on either jQuery or Bootstrap Native. On DOMContentLoaded, useBSN is set true if window.BSN is detected and useBSNv2 is set true if useBSN and window.BSN.Modal is undefined.

message-modal.js
'use strict'
/*!
* Copyright © 2020 Ken Haggerty (https://kenhaggerty.com)
* Licensed under the MIT License.
* Bootstrap 4 Message Modal - Version 1.0.3
* Requires BootstrapNative or jQuery
*/
var defaultMessage = 'Thank you for supporting KenHaggerty.Com.';
var defaultMessageClass = 'alert-primary';

var defaultMessageAllowDismiss = true;
var defaultMessageDismissText = 'OK';
var defaultMessageDismissClass = 'btn-primary';

var defaultMessageIncludeLink = false;
var defaultMessageLinkHref = 'https://kenhaggerty.com';
var defaultMessageLinkTarget = '_self';
var defaultMessageLinkText = 'KenHaggerty.Com';
var defaultMessageLinkClass = 'btn-primary';

var defaultMessageHideBackdrop = false;

let useBSN = false;
let modalBSN = null;

let modalBodyDiv = document.createElement('div');
modalBodyDiv.classList.add('modal-body');
modalBodyDiv.classList.add('text-center');

let messageElement = document.createElement('h4');
let messageClassDiv = document.createElement('div');
messageClassDiv.appendChild(messageElement);
modalBodyDiv.appendChild(messageClassDiv);

let messageLinkButton = document.createElement('a', { href: '/' });
modalBodyDiv.appendChild(messageLinkButton);

let messageDismissButton = document.createElement('button', { type: 'button' });
messageDismissButton.dataset.dismiss = 'modal';
modalBodyDiv.appendChild(messageDismissButton);

let modalContentDiv = document.createElement('div');
modalContentDiv.classList.add('modal-content');
modalContentDiv.appendChild(modalBodyDiv);

let modalDialogDiv = document.createElement('div', { role: 'document' });
modalDialogDiv.classList.add('modal-dialog');
modalDialogDiv.classList.add('top-50');
modalDialogDiv.appendChild(modalContentDiv);

let modalDiv = document.createElement('div', { tabindex: '-1', role: 'dialog', ariaHidden: "true" });
modalDiv.classList.add('modal');
modalDiv.classList.add('message-modal');
modalDiv.classList.add('fade');
modalDiv.appendChild(modalDialogDiv);

document.body.appendChild(modalDiv);

function showMessageModal(
    message = defaultMessage,
    messageClass = defaultMessageClass,
    allowDismiss = defaultMessageAllowDismiss,
    dismissText = defaultMessageDismissText,
    dismissClass = defaultMessageDismissClass,
    includeLink = defaultMessageIncludeLink,
    linkHref = defaultMessageLinkHref,
    linkTarget = defaultMessageLinkTarget,
    linkText = defaultMessageLinkText,
    linkClass = defaultMessageLinkClass,
    hideBackdrop = defaultMessageHideBackdrop) {

    // modalBSN gets re-initialized
    if (!useBSN) {
        $('.message-modal').modal('dispose');
    }

    if (message.length === 0) message = defaultMessage;
    let messageParse = message.toString();
    if (messageParse.indexOf('DOMException') !== -1) message = messageParse.substring(14);
    if (messageParse.indexOf('NotAllowedError') !== -1) message = messageParse.substring(17);
    if (messageClass.length === 0) messageClass = defaultMessageClass;
    if (dismissText.length === 0) dismissText = defaultMessageDismissText;
    if (dismissClass.length === 0) dismissClass = defaultMessageDismissClass;
    if (linkHref.length === 0) linkHref = defaultMessageLinkHref;
    if (linkTarget.length === 0) linkTarget = defaultMessageLinkTarget;
    if (linkText.length === 0) linkText = defaultMessageLinkText;
    if (linkClass.length === 0) linkClass = defaultMessageLinkClass;

    messageElement.innerText = message;

    messageClassDiv.classList.remove(...messageClassDiv.classList);
    messageClassDiv.classList.add(messageClass);
    messageClassDiv.classList.add('px-2');
    messageClassDiv.classList.add('py-1');
    messageClassDiv.classList.add('mb-3');

    messageDismissButton.textContent = dismissText;
    messageDismissButton.classList.remove(...messageDismissButton.classList);
    messageDismissButton.classList.add('btn');
    messageDismissButton.classList.add(dismissClass);

    messageLinkButton.classList.add('d-none');

    if (includeLink) {
        messageLinkButton.setAttribute('href', linkHref);
        messageLinkButton.setAttribute('target', linkTarget);
        messageLinkButton.textContent = linkText;
        messageLinkButton.classList.remove(...messageLinkButton.classList);
        messageLinkButton.classList.add('btn');
        messageLinkButton.classList.add(linkClass);

        if (allowDismiss) {
            messageLinkButton.classList.add('mr-2');
        } else {
            messageDismissButton.classList.add('d-none');
            if (useBSN) {
                modalBSN = new BSN.Modal(modalDiv, { backdrop: 'static', keyboard: false });
                modalBSN.show();
            } else {
                $('.message-modal').modal({ backdrop: 'static', keyboard: false });
            }
            return;
        }
    }

    if (hideBackdrop) {
        if (useBSN) {
            modalBSN = new BSN.Modal(modalDiv, { backdrop: false });
            modalBSN.show();
        } else {
            $('.message-modal').modal({ backdrop: false });
        }
    } else {
        if (useBSN) {
            modalBSN = new BSN.Modal(modalDiv);
            modalBSN.show();
        } else {
            $('.message-modal').modal();
        }
    }
}

document.addEventListener('DOMContentLoaded', function () {
    if (window.jQuery) {
        if (!$.fn.modal) { alert('Bootstrap-jQuery modal was not found.'); }
    } else if (window.BSN) {
        useBSN = true;
        if (!window.BSN.Modal) { alert('BootstrapNative Modal was not found.'); }
    } else { alert('BootstrapNative or jQuery is required by the Bootstrap Message Modal.'); }
}, false);

The project includes a Modal Message Generator which dynamically creates the minimal signature for the showMessageModal function with examples.

Modal Message Generator
Update 05/10/2020

I updated the article links.

Update 05/23/2020

I updated message-modal.js to allow line breaks in the message, added the AES Cipher article link and updated the screenshot.

Update 06/09/2020

I updated the article to announce support for Bootstrap Native v3 and updated message-modal.js to v1.0.2.

Update 06/30/2020

I updated the message-modal.js to v1.0.3.



Comment Count = 0

Please log in to comment or follow.

Login Register
Follow to get web notifications when new comments are posted to this article.
Logged in users receive web notifications for new articles, topics and assets.
Web Notifications