Advertisement

#10 Our Inputs and Buttons Need More Color

In this article we will add the ability for our inputs and buttons to update their appearance based on the underlying state of our typescript class by applying css classes. Although the situation is a bit more complicated than the icons for our validators we will use the same approach where we bind a class property on our template to methods in our typescript class.

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

First Correction of a Correction

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

We all knew that it was only a matter of time didn't we? Here we have our first correction of a correction. In part 6 I adjusted the size of the icon and now it's time to take that back. Although I still think visually the size increase is good I have decided that we should transition our validator icons from one to another instead of just abruptly changing them. So when we transition from invalid to valid the icon does a little jitter bug dance as the browser interpolates the font-size from 1.3 em to 1 em. The dance is completely unacceptable where I found the change of size to only be a slight benefit so we will remove the font-size modification to the icon shown in (a).

authenticator.component.scss (correction)

.authenticator {
    ...
    .info-validator {
        ...
        .validator-icon {
            ...
            &.fa-times {
                color: $invalid-color;
                font-size: 1.3em; // <- Remove this line
            }
        }
    }
}
(a) It was good while it lasted. Just need to remove the adjustment to the font-size of the icon.

Time for more dynamic css classes

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

In previous articles we dynamically adjusted the icons for our validators by applying different css classes to them. Now it is time to use the same approach to update the appearance of our form control groups. Our first step is to modify our template (b) so that we can apply conditional classes to the input group and button icon. The logic for determining the appropriate class for the input group and button icon is too complicated to be done within the template so we will use methods on our typescript class to do it. Next we just need to add a couple of styles to our component's scss file. One style for input groups that are awaiting confirmation, we will find out what that means shortly, and one for the aforementioned transitioning between icons for our validators (c). Finally we need to add the referenced methods to our typescript class (d). In order to see that our changes have been applied we will return valid for the input group instead of invalid and fa-check for our button icons instead of fa-times.

authenticator.component.pug

mixin formControlGroup(label, inputType, key)
    div.form-control-group
        ...
        div.input-group([ngClass]=`getInputGroupClass('${key}')`)
            ...
            button(type="button")
                i.fa([ngClass]=`getInputGroupButtonIconClass('${key}')`)
...
div.authenticator
    div.inputs-container#inputs-container
        h3 {{title}}
(b) Just need to make a few simple changes to our form control group mixin to start the process of applying dynamic classes.

authenticator.component.scss

$awaiting-confirmation-color: #E87E04 !default;

.authenticator {
    .input-group {
        &.awaiting-confirmation {
            @include inputGroup($awaiting-confirmation-color);

            @keyframes awaiting-confirmation-btn {
                0% {
                    background-color: $awaiting-confirmation-color;
                }

                50% {
                    background-color: lighten($awaiting-confirmation-color, 10);
                }

                100% {
                    background-color: $awaiting-confirmation-color;
                }
            }

            button.valid {
                animation-name: awaiting-confirmation-btn;
                animation-iteration-count: infinite;
                animation-duration: 1.5s;
            }
        }
    }

    .validator-icon {
        transition: all 0.5s;
    }
}
(c) The styles that we need in order to achieve the look and behaviour for our inputs and buttons as well as allowing the changes to our validator icons to transition smoothly.

authenticator.component.ts (1)

export class AuthenticatorComponent implements OnInit {
    ...
    public get title() {
        if (this.isLogin) {
            if (this.isSubActionForgotPassword) {
                return "Forgot Password";
            }
            if (this.isSubActionForgotUsername) {
                return "Forgot Username";
            }
            return "Login";
        }
        if (this.isRegister) {
            return "Register";
        }
        throw new Error("Application is not logging in or registering.");
    }
    ...
    public getInputGroupClass = (key: string): string => {
        return "valid";
    }

    public getInputGroupButtonIconClass = (key: string): string => {
        return "fa-check";
    }
    ...
}
(d) Temporary logic to test that we have wired up our methods correctly.

If everything has gone as planned then once we save our changes and refresh the browser our interface should go from (e) to (f).

Before our updates the inputs and buttons were colored red and
            the button icon was a x indicating the group was invalid.
(e) Before our updates the inputs and buttons were colored red and the button icon was a indicating the group was invalid.
After our update the inputs and buttons are colored green and
            the button icon is a checkmark indicating the group is valid.
(f) After our update the inputs and buttons are colored green and the button icon is a indicating the group is valid.

Is valid or invalid enough

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.ts

In the previous section we created the ability for applying css classes to various elements of the ui. Unfortunately those classes were still static and that of course is not very helpful to achieving what we are trying to do. As our next step in the process we are going to use the status of the corresponding control to determine what class(es) should be returned as shown in (g).

authenticator.component.ts (2)

export class AuthenticatorComponent implements OnInit {
    ...
    public getInputGroupClass = (key: string): string => {
        const control = this[key] as FormControl;
        return control.valid ? "valid" : "invalid";
    }

    public getInputGroupButtonIconClass = (key: string): string => {
        const control = this[key] as FormControl;
        return control.valid ? "fa-check" : "fa-times";
    }
    ...
}
(g) Modification of our methods that uses the status of the control to determine what css class should be returned.
Image showing the username control group is invalid while at least one
            validator was still invalid.
(h) Image showing the username control group is invalid while at least one validator was still invalid.
Image showing the username control group is valid when all of the
            validators are valid.
(i) Image showing the username control group is valid when all of the validators are valid.
Advertisement

Finish up the input group

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.ts

We are now return the css classes dynamically but of course life is not simple enough for us to get away with just caring about whether a control is valid or not. For example when the page is first loaded the controls are all invalid but we do not want them to start out showing the user that they are invalid before they interact with them. Furthermore in the case of the username the behaviour is different depending on whether the user is attempting to login or register. In (j) we are accounting for the behaviours that we have just talked about as well as a few more.

authenticator.component.ts (3)

export class AuthenticatorComponent implements OnInit {            
    ...
    private _isUsernameUnique: boolean = null;
    ...
    public getInputGroupClass = (key: string): string => {
        const control = this[key] as FormControl;

        if (typeof control === "undefined" || control === null) {
            throw new Error(`Error getting input group class for key: ${key}.`);
        }

        switch (key) {
            case this.controlUsernameKey:
                if (control.pristine || (this.isRegister && this._isUsernameUnique === null)) {
                    return "indeterminate";
                }
                return control.valid ? "valid" : "invalid";
            default:
                if (this.isLogin) {
                    return control.pristine
                        ? "indeterminate"
                        : control.valid ? "valid" : "invalid";
                } 
                if (key === this.controlPasswordKey || key === this.controlEmailKey) {
                    return control.pristine
                        ? "indeterminate"
                        : control.valid ? "awaiting-confirmation" : "invalid";
                }
                return control.valid ? "valid" : "invalid";
        }
    }
    ...
}
(j) Code required to dynamically determine the css class for an input group.

With the code in (j) if the user is attempting to login all of the controls are indeterminate until the user interacts with them removing the pristine status. For registering the confirm controls start out invalid until the user matches them with their corresponding initial input.

All of the controls now show that they are set to the indeterminate
            state when they are pristine.
(k) All of the controls now show that they are set to the indeterminate state when they are pristine.
For registering all of the controls are indeterminate when the page is first loaded
            except for the two used for confirmation which are invalid.
(l) For registering all of the controls are indeterminate when the page is first loaded except for the two used for confirmation which are invalid.

Finish up the button icon

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.ts

With the input group now having one or more css classes applied dynamically we now need to deal with the button icon. Of course if the user is logging in it is a straight forward matter of determining the icon based on whether the control is valid. On the other hand if the user is registering we need to take into account whether the button is a confirm button or not (m). And of course we have a few other factors to think about such as whether or not our component is checking the username availability with the server but we will talk more about that in a future article.

authenticator.component.ts (4)

export class AuthenticatorComponent implements OnInit {
    ...
    private _isCheckingUsernameAvailability: boolean = false;
    ...
        public getInputGroupButtonIconClass = (key: string): string => {
        const control = this[key] as FormControl;

        if (typeof control === "undefined" || control === null) {
            throw new Error(`Error getting icon class for key: ${key}.`);
        }

        if (this.isLogin) {
            return control.valid ? "fa-check" : "fa-times";
        }
        switch (key) {
            case this.controlUsernameKey:
                if (control.pristine || this._isUsernameUnique === null) {
                    let cssClass = "fa-circle-o-notch";
                    if (this._isCheckingUsernameAvailability) {
                        cssClass = `${cssClass} fa-spin`;
                    }
                    return cssClass;
                }
                return control.valid ? "fa-check" : "fa-times";
            default:
                if (key === this.controlPasswordKey || key === this.controlEmailKey) {
                    return control.valid ? "fa-arrow-right" : "fa-times";
                }
                return control.valid ? "fa-check" : "fa-arrow-left";
        }
    }
    ...
}
(m) Updating the code to determine the icon of our input group button.

After the update we see that if the user successfully validates the first of a pair of inputs that the icon and color now correctly update as well.

Image showing that if the first of a pair of inputs validates then the
            icon and color now correctly update.
(n) Image showing that if the first of a pair of inputs validates then the icon and color now correctly update.
Exciton Interactive LLC
Advertisement