Advertisement

#5 Authenticator Validators

In this article we will finish the ui of our authenticator component by creating and styling the validators for our inputs. Once we have completed this we will also create a service for parsing a url. We will import this service into our component and we will eventually use it to tell whether the user is attempting to login or register.

You can view a completed example of the authenticator component here.

Info pages

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.pug
          • authenticator.component.scss

For the right hand side of our component we will have the concept of an info page. An info page consists of a heading and several statement, usually notification of pass or failure of a particular validator. In (a) we see examples of two information pages, the first starting with the 'Welcome!' heading and the other starting with the 'Username' heading. When we are done we will have a default page, which is the welcome page, and a page for each control which will be displayed when the corresponding control has focus.

authenticator.component.pug (1)

mixin formControlGroup(label)
    div.form-control-group
        label #{label}
        div.input-group.invalid
            input
            button
                i.fa.fa-times

div.authenticator
    div.inputs-container
        h3 Login
        form(novalidate)
            +formControlGroup("Username")

            div.forgot
                a Forgot your username?
            
            div.dual-input-group
                +formControlGroup("Password")
                +formControlGroup("Confirm Password")

            div.forgot
                a Forgot your password?

            div.dual-input-group
                +formControlGroup("Email")
                +formControlGroup("Confirm Email")

            div.form-control-group.control-group-remember-me
                input#remember-me(type="checkbox")
                label(for="remember-me")
                    span #[i.fa]
                    | Remember me?

            div.input-buttons
                button Reset
                button(disabled="true") Send
    div.info-container
        div.info-pages
            div.info
                h3.info-heading Welcome!
                div.info-validator-container
                    div.info-validator
                        p.validator Hello again, we are happy to see you. Please enter your username and password to login.
            div.info
                h3.info-heading Username
                div.info-validator-container            
                    div.info-validator
                        p.validator Lorem ipsum dolor sit amet
                        i.validator-icon.fa.fa-times.invalid
                    div.info-validator
                        p.validator Lorem ipsum dolor sit amet
                        i.validator-icon.fa.fa-times.invalid            
                    div.info-validator
                        p.validator Lorem ipsum dolor sit amet
                        i.validator-icon.fa.fa-times.invalid
        div.need-to-login
            a Already have an account?
        div.need-to-register
            a Need to register an account?
(a) Time to add the info pages to the right hand side of our ui.

authenticator.component.scss (1)

$info-heading-background-color: darken($info-container-background-color, 2%);

.authenticator {
    .info-container {
        display: flex;
        flex-direction: column;
        color: $info-container-color;
    }

    .info-pages {
        flex: 1;
    }

    .need-to-login, .need-to-register {
        text-align: right;
        padding: em(10px);

        a {
            color: $info-container-color;
        }
    }

    .info-heading {
        padding: em(10px);
        background-color: $info-heading-background-color;
        border-bottom: 1px solid #3b3b3b;

        h3 {
            margin-bottom: 0;
        }
    }

    .info-validator {
        display: flex;
        flex-direction: row;
        margin-bottom: em(4px);
        padding: em(5px) em(10px);

        &:nth-child(2n) {
            background-color: $info-heading-background-color;
        }

        .validator {
            margin-bottom: 0;
            flex: 1;
            color: $info-container-color;
        }

        .validator-icon {
            padding-top: em(2px);

            &.fa-circle-o-notch {
                color: $indeterminate-color;
            }
    
            &.fa-check {
                color: $valid-color;
            }
    
            &.fa-times {
                color: $invalid-color;
                font-size: 1.3em;
            }
        }
    }
}
(b) Styles to format our info pages.

We are also adding links for users to be able to change from login to register at the bottom of the info pages side of the component.

A couple of info pages added to the right half of the ui.
(c) Our component now has info pages on the right half the ui.

Info Pages Mixins

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.pug

In (a) we see that .info, .validator, and the .validator-icon classes are used repeatedly and would benefit greatly from mixins of their own. So in (a) we have added the info and validator mixins. We are splitting it into two different mixins because each info page will potentially contain a set of validators that is different than every other page.

authenticator.component.pug (2)

mixin formControlGroup(label)
    div.form-control-group
        label #{label}
        div.input-group.invalid
            input
            button
                i.fa.fa-times

mixin info(heading)
    div.info
        h3.info-heading #{heading}
        div.info-validator-container
            block

mixin validator(message)
    div.info-validator
        p.validator #{message}
        i.validator-icon.fa.fa-times.invalid

div.authenticator
    div.inputs-container
        h3 Login
        form(novalidate)
            +formControlGroup("Username")

            div.forgot
                a Forgot your username?
            
            div.dual-input-group
                +formControlGroup("Password")
                +formControlGroup("Confirm Password")

            div.forgot
                a Forgot your password?

            div.dual-input-group
                +formControlGroup("Email")
                +formControlGroup("Confirm Email")

            div.form-control-group.control-group-remember-me
                input#remember-me(type="checkbox")
                label(for="remember-me")
                    span #[i.fa]
                    | Remember me?

            div.input-buttons
                button Reset
                button(disabled="true") Send
    div.info-container
        div.info-pages
            +info("Welcome!")
                div.info-validator
                    p.validator Hello again, we are happy to see you. Please enter your username and password to login.
            +info("Username")
                +validator("Lorem ipsum dolor sit amet")
                +validator("Lorem ipsum dolor sit amet")
                +validator("Lorem ipsum dolor sit amet")

        div.need-to-login
            a Already have an account?
        div.need-to-register
            a Need to register an account?
(d) Adding the info and validator mixins makes it easier to add info pages.

The final set of info pages

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.pug

We have already added the default and username info pages but we need to add a few more. In particular we need to add a page for: password, confirm password, email and confirm email controls. We can do this very simply by making calls to the mixins that we created earlier. Soon we will add actual validators to these pages for the corresponding controls but for now the temporary text will do. Once we have added interaction to our component we will stack all of the pages on-top of each other so that we can fade from one to the other in the same location.

authenticator.component.pug (3)

mixin formControlGroup(label)
    div.form-control-group
        label #{label}
        div.input-group.invalid
            input
            button
                i.fa.fa-times

mixin info(heading)
    div.info
        div.info-heading
            h3 #{heading}
        div.info-validator-container
            block

mixin validator(message)
    div.info-validator
        p.validator #{message}
        i.validator-icon.fa.fa-times.invalid

div.authenticator
    div.inputs-container
        h3 Login
        form(novalidate)
            +formControlGroup("Username")

            div.forgot
                a Forgot your username?
            
            div.dual-input-group
                +formControlGroup("Password")
                +formControlGroup("Confirm Password")

            div.forgot
                a Forgot your password?

            div.dual-input-group
                +formControlGroup("Email")
                +formControlGroup("Confirm Email")

            div.form-control-group.control-group-remember-me
                input#remember-me(type="checkbox")
                label(for="remember-me")
                    span #[i.fa]
                    | Remember me?

            div.input-buttons
                button Reset
                button(disabled="true") Send
    div.info-container
        div.info-pages
            div.info
                div.info-heading
                    h3 Welcome!
                div.info-validator-container
                    div.info-validator
                        p.validator Hello again, we are happy to see you. Please enter your username and password to login.
            +info("Username")
                +validator("Lorem ipsum dolor sit amet")
                +validator("Lorem ipsum dolor sit amet")
                +validator("Lorem ipsum dolor sit amet")

            +info("Password")
                +validator("Lorem ipsum dolor sit amet")

            +info("Confirm Password")
                +validator("Lorem ipsum dolor sit amet")

            +info("Email Address")
                +validator("Lorem ipsum dolor sit amet")

            +info("Confirm Email Address")
                +validator("Lorem ipsum dolor sit amet")

        div.need-to-login
            a Already have an account?
        div.need-to-register
            a Need to register an account?
(e) Added all of the required info pages to our component.
Component ui with all of the info pages added.
(f) Component ui with all of the info pages added.
Advertisement

Actual Validators

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.pug

Now that we have added all of the info pages that we need it's time to remove the temporary validators and replace them with the actual validators that we will be needing. At this point though this just means adding the correct number to each page and replacing their temporary messages with real messages.

authenticator.component.pug (4)

mixin formControlGroup(label)
    div.form-control-group
        label #{label}
        div.input-group.invalid
            input
            button
                i.fa.fa-times

mixin info(heading)
    div.info
        div.info-heading
            h3 #{heading}
        div.info-validator-container
            block

mixin validator(message)
    div.info-validator
        p.validator #{message}
        i.validator-icon.fa.fa-times.invalid

div.authenticator
    div.inputs-container
        h3 Login
        form(novalidate)
            +formControlGroup("Username")

            div.forgot
                a Forgot your username?
            
            div.dual-input-group
                +formControlGroup("Password")
                +formControlGroup("Confirm Password")

            div.forgot
                a Forgot your password?

            div.dual-input-group
                +formControlGroup("Email")
                +formControlGroup("Confirm Email")

            div.form-control-group.control-group-remember-me
                input#remember-me(type="checkbox")
                label(for="remember-me")
                    span #[i.fa]
                    | Remember me?

            div.input-buttons
                button Reset
                button(disabled="true") Send
    div.info-container
        div.info-pages
            div.info
                div.info-heading
                    h3 Welcome!
                div.info-validator-container
                    div.info-validator
                        p.validator Hello again, we are happy to see you. Please enter your username and password to login.
            +info("Username")
                +validator("Unique")
                +validator("Required")
                +validator("Minimum length is 8")
                +validator("Maximum length is 100")
                +validator("May only contain alphanumeric and '-._@+' characters")

            +info("Password")
                +validator("Required")
                +validator("Minimum length is 12")
                +validator("Maximum length is 100")
                +validator("Must contain at least one lowercase letter")
                +validator("Must contain at least one capital letter")
                +validator("Must contain at least one digit")
                +validator("Must contain at least one character that is not a letter or number")

            +info("Confirm Password")
                +validator("The password and confirm password must match.")

            +info("Email Address")
                +validator("Required")
                +validator("Must be a valid email address")

            +info("Confirm Email Address")
                +validator("The email address and confirm email address must match.")

        div.need-to-login
            a Already have an account?
        div.need-to-register
            a Need to register an account?
(g) Updated our info pages to contain the actual validators for our component.
A couple of info pages added to the right half of the ui.
(h) Display of all of our inputs and info pages with the actual validators.

Url Parsing

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.ts
      • services
        • uri-parser.service.ts

Finally we will add a small service (i) that we will use to parse the current url of the browser. As I have mentioned before we will use the url to determine whether or not our component should be configured for logging in or registering. The code that we will be using for this has been slightly adapted from code written by Steven Levithan which you can find here. Once we have the parser service in place we can then import it into our authenticator.component.ts file (j). By adding a reference to the UriParser in the metadata of our component, within the newly added providers array, angular will inject an instance into our component constructor.

uri-parser.service.ts

export interface IUriParserResult {
    anchor: string;
    authority: string;
    directory: string;
    file: string;
    host: string;
    password: string;
    path: string;
    port: string;
    protocol: string;
    query: string;
    queryKey: any;
    relative: string;
    source: string;
    user: string;
    userInfo: string;
}

export class UriParserService {
    /**
     * The parse options
     */
    public options = {
        strictMode: false,
        key: ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"],
        q: {
            name: "queryKey",
            parser: /(?:^|&)([^&=]*)=?([^&]*)/g
        },
        parser: {
            strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
            loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
        }
    }

    public parseUri =
    /**
     * Parses the provide string.
     * @param str 
     * @returns {} 
     */
    (str: string): IUriParserResult => {
        const o = this.options;
        const m = o.parser[o.strictMode ? "strict" : "loose"].exec(str);
        const uri = {};
        let i = 14;

        while (i--) uri[o.key[i]] = m[i] || "";

        uri[o.q.name] = {};
        uri[o.key[12]].replace(o.q.parser, ($0: any, $1: any, $2: any) => {
            if ($1) uri[o.q.name][$1] = $2;
        });

        return uri as IUriParserResult;
    };
}
(i) The parser service that we will use to parse the page's url in order to determine if the component should be logging in or registering.

authenticator.component.ts

import { Component } from "@angular/core";

import { UriParserService } from "../../services/uri-parser.service";

@Component({
    selector: "authenticator",
    template: require("./authenticator.component.pug"),
    styles: [require("./authenticator.component.scss")],
    providers: [UriParserService]
})
export class AuthenticatorComponent {
    constructor(private _uriParser: UriParserService) {
        console.log(this._uriParser.parseUri(window.location.href));
    }
}
(j) Updating the authenticator component's typescript file to include the uri parser service.

In (k) we see the result of parsing of a url using our uri parsing service. From the results we can see that there are several properties that would can use to determine if the user is trying to login or register with our application. In the end we will probably just parse the path property.

(k) The result of parsing the window.location.href property.

In the next video we will begin the process of adding user interaction to our component by creating the means of applying conditional css classes to our template.

Exciton Interactive LLC
Advertisement