Quantcast
Channel: Adrian Hall » ecmascript6
Viewing all articles
Browse latest Browse all 10

ASP.NET MVC6 Identity Part 3 – An ECMAScript 6 Modal Login Dialog

$
0
0

I’ve recently been recoding my ASP.NET Identity tutorial to use WebAPI and conform to any changes introduced in the beta-4 release of ASP.NET5 MVC6. In the last article I introduced the WebAPI for logging in, but I was testing it with Postman. I don’t expect my users to use Postman to log in so I need a dialog.

Today I am concentrating on the modal dialog. When I click on the Sign In link, I want a modal dialog with the login form to appear. The modal dialog will have a close button and when I click that, I want the modal dialog to go away. I want all the code to be in ECMAScript 6 and ideally not rely on jQuery so I can potentially re-use the code without too much bother.

Signing Out

Before I start, I noticed that I was signed in permanently from the work during the last article. I need to be able to sign out as well. To do this, I’m going to add a normal MVC action to the AccountController that signs the user out and then redirects to the home page:

        /// <summary>
        /// Sign out of the web application
        /// </summary>
        /// redirect to the home page
        [HttpGet]
        public IActionResult Logout()
        {
            signInManager.SignOut();
            return RedirectToAction("Index", "Home", new { area = "Public" });
        }

Don’t forget to add the [Area("Public")] to the class to ensure that the routing happens properly. Once this is done, you should be able to click on the Sign Out link on the top banner.

The Login Dialog HTML

My Areas/Public/Views/Shared/Header.cshtml contains the menu links along the top of the page. I want to introduce another Partial View to include my login dialog HTML at the right place:

<header>
    <h1>
        <a href="@Url.Action("Index","Home", new { area = "Public" })">Grumpy Wizards</a>
    </h1>
    <nav>
        <ul>
            @if (User.Identity.IsAuthenticated)
            {
                <li id="sign-out">
                    <a href="@Url.Action("Logout","Account", new { area = "Public" })">
                        <i class="fa fa-sign-out"></i> Sign Out
                    </a>
                </li>
            }
            else
            {
                <li id="signInActivator">
                    <i class="fa fa-sign-in"></i> Sign In
                    @Html.Partial("~/Areas/Public/Views/Account/LoginModal.cshtml")
                </li>
            }
        </ul>
    </nav>
</header>

I’ve included the Partial View right in line with the link, and it is going to be included in a file called LoginModal.cshtml. The directory Areas/Public/Views/Account doesn’t exist yet, so make sure you create it. Here is the LoginModal.cshtml file:

<div  id="loginModal" class="modalWrapper">
    <div class="modalForm">
        <div class="modalClose">X</div>
        <h1>Enter Credentials</h1>
        <form>
            <div class="modalFormError" id="errorForSignInModal"></div>
            <div class="modalFormGroup">
                <span class="modalFormLabel">
                    <i class="fa fa-envelope"></i>
                </span>
                <span class="modalFormControl">
                    <input type="text" name="Email" placeholder="Email Address">
                </span>
            </div>
            <div class="modalFormError" id="errorForEmail"></div>
            <div class="modalFormGroup">
                <span class="modalFormLabel">
                    <i class="fa fa-key"></i>
                </span>
                <span class="modalFormControl">
                    <input type="password" name="Password">
                </span>
            </div>
            <div class="modalFormError" id="errorForPassword"></div>
            <div class="modalFormGroup">
                <button type="button" id="loginButton">Login</button>
            </div>
        </form>
    </div>
</div>

Note that I am not using a view modal nor am I using the Razor HTML helpers here. This is all pure HTML. That is because I am going to be handling everything client side and only submitting the form once I’ve got everything validated. Also, I’m not using a submit button. I’m using a regular button. This will mean that my form doesn’t get submitted when I click on the button and that means I can handle the request with Javascript instead.

Styling the Modal

I’ve created a new file Static/less/modal.less with the modal dialog styling in it. I’ve included the styling in the main site.less like this:

@import (reference) "inc/utils.less";
@import (less) "settings.less";

html {
    .fullsize();

    body {
        .fullsize();
        padding-top: @header-h;
    }
}

@import (less) "modal.less";
@import (less) "header.less";
@import (less) "signin.less";

The signin.less file contains the styling for the login form. I’m going to produce some common styling in modal.less since I probably will be doing a few other modal dialogs. Here is the modal.less file:

@import (reference) "settings.less";

/*
    Modal Dialog Overlay
*/
.modalWrapper {
    // The modal wrapper covers the entire page
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: rgba(0,0,0,0.8);
    z-index: 99999;
    transition: opacity 400ms ease-in;
    pointer-events: none;

    // The form is a 400px wide block in the center
    .modalForm {
        width: 400px;
        position: relative;
        margin: 10% auto;
        border-radius: 10px;
        background: @color-5;
        pointer-events: auto;

        // The close button appears in the top left corner
        .modalClose {
            background: @color-3;
            color: @color-4;
            line-height: 25px;
            position: absolute;
            right: -12px;
            top: -10px;
            text-align: center;
            width: 24px;
            text-decoration: none;
            font-weight: bold;
            border-radius: 12px;
            box-shadow: 1px 1px 3px #000;
            cursor: pointer;

            &:hover {
                background: @color-2;
                color: @color-4;
            }
        }
    }
}

The modal wrapper (that is, the outer most DIV of the modal) takes up the entire screen and traps any pointer events. The modal inner (class="modalForm") is restores the pointer events and is styled to look like a modal. Finally, I’ve got a close button that will appear in the top left corner of the dialog. It is positioned absolutely but relative to its parent.

The Login Dialog Javascript

I’ve already got an app-main.js file that gets run when the application initializes, so I’m going to bring the login dialog during that phase:

/**
 * Main Application Code
 */
/*eslint no-console:0 */

"use strict";

import { LoginModal } from "./loginModal";

$(document).ready(function() {
    var elements = [];

    /**
     * Initialize the login modal dialog
     */
    function initializeLoginModal() {
        let modalActivator = document.getElementById("signInActivator"),
            modalDialog = document.getElementById("loginModal");

        if (modalActivator == null || modalDialog == null) {
            console.info("[app-main] Login Mode is not available - skipping Login Modal Initialization");
            return;
        }

        console.info("[app-main] Initializing Login Modal");
        var loginModal = new LoginModal(modalActivator, modalDialog);
        elements.push(loginModal);
    }


    console.info("[app-main] Initializing Application...");
    initializeLoginModal();
});

This relies on jQuery to do wrap the code so that it doesn’t execute until the page is loaded. The main piece of initialization is in the initializeLoginModal() function. First off, it finds the activator link (the Sign In block in the top header) and the modal dialog DIV. If they are both available, then it creates a new LoginModal object and pushes it onto the elements stack (which is just an array of things I create). If the two elements are not available, then the routine just returns.

The major ECMAScript 6 code is in the loginModal.js code, which contains a LoginModal class that it exports:

/**
 * Handler for the Login Modal
 */

"use strict";

class LoginModal {
    /**
     * Constructor - store the ID that activates us and then
     * wire up the activator.
     */
    constructor(activatorEl, modalEl) {
        // Store all the elements for later
        this.activator = activatorEl;
        this.modal = modalEl;
        this.modalClose = modalEl.querySelector(".modalClose");

        // Make the activatorcursor a pointer to show it is clickable
        this.activator.style.cursor = "pointer";

        // Wire up the click event handlers for the activator and modal close
        this.activator.addEventListener("click", (e) => { this.activate(e); }, false);
        this.modalClose.addEventListener("click", (e) => { this.close(e); }, false);

        // Ensure the modal is hidden
        this.modal.style.display = "none";
        return;
    }

    /**
     * Someone clicked on the activator - toggle the visibility
     */
    activate(e) {
        this.modal.style.display = "block";
        e.stopPropagation();
    }

    /**
     * Someone clicked on the close icon - hide the modal
     */
    close(e) {
        this.modal.style.display = "none";
        e.stopPropagation();
    }
}

export { LoginModal }

This is an ECMAScript 6 class inside an ECMAScript 6 module, using ECMAScript 6 fat arrows to preserve the object reference during an event handler. There are two event handlers – one to activate (or show) the dialog and one to close it. I’ve wired up the activate event handler to the click event of the Sign In link and the close event handler to the click event of the close icon in the top-left hand corner of the dialog.

Adjusting ESLint for ES6 Modules

If you run all this code, you will notice a failure on transpiling the ES6 code in ESLint. It will say something like “Illegal import declaration” or “Illegal export declaration”. I haven’t told ESLint I am using ES6 modules. This is, surprisingly, not done in the gulp-eslint module. Rather, it is done in package.json with the following declaration:

  "eslintConfig": {
    "ecmaFeatures": {
      "modules": true
    }
  },

Once you have this in your package.json, the transpile step should not produce any errors.

Finishing Up

I also added a signin.less file to style the actual sign-in form. Once that is included, you should be able to click on the Sign In link and get the modal dialog, then click on the close icon and see it close again. Since it’s modal, you should not be able to click on anything other than the modal piece.

You can download all the code, of course, from my GitHub Repository.


Filed under: Web Development Tagged: asp.net, ecmascript6, es6, eslint

Viewing all articles
Browse latest Browse all 10

Trending Articles