ASP.NET Core 3.1 - Message Modal


Ken Haggerty
Created 05/08/2020 - Updated 05/23/2020 15:17

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

This project helps mitigate some of the issues implementing the challenge only. The Users Without Passwords Project implements the challenge with users and authenticators.


Access to the research project source code is free to registered users 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.

jQuery's Total Size = 168,843‬ bytes
  • jquery.min.js v3.4.1 = 88,145 bytes
  • bootstrap.bundle.min.js v4.4.1 = 80,698 bytes
BootstrapNative's Total Size = 28,624‬ bytes
  • bootstrap.native-v4.min.js v2.0.27 = 22,295 bytes
  • pollyfill.min.js = 3,599 bytes
  • [if lt IE 9] html5shiv.min.js v3.7.3 = 2,730 bytes

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 BootstrapNative. KenHaggerty.Com implements BootstrapNative and ASP.NET Core 2.2 - BootstrapNative Client Validation. The FIDO Utilities Project implements libman.json and includes BootstrapNative and jQuery with validation. I added a bootstrap-native-v4. min. js link with CDN and fallback and commented the jQuery links in _Layout. cshtml. See more on libman.json at ASP.NET Core 2.2 - Manage Client-Side Libraries.

The message-modal.js will support either jQuery or BootstrapNative. On DOMContentLoaded, useBSN is set true if window.BSN is detected.

message-modal.js
// Copyright © 2020 Ken Haggerty (https://kenhaggerty.com)
// Licensed under the MIT License.
// Bootstrap 4 Message Modal - Version 1.0.1
// 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('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) $('.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 Modal(modalDiv, { backdrop: 'static', keyboard: false });
                modalBSN.show();
            } else
                $('.modal').modal({ backdrop: 'static', keyboard: false });

            return;
        }
    }

    if (hideBackdrop) {
        if (useBSN) {
            modalBSN = new Modal(modalDiv, { backdrop: false });
            modalBSN.show();
        } else
            $('.modal').modal({ backdrop: false });
    } else {
        if (useBSN) {
            modalBSN = new Modal(modalDiv);
            modalBSN.show();
        } else  $('.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.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.



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