#10 Our Inputs and Buttons Need More Color
Friday, December 8, 2017
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.
Parts
- Part 30: User Database
- Part 29: Canonical Url
- Part 28: Razor Page Handlers
- Part 27: Send Screen Finished
- Part 26: Stroke Dashoffset
- Part 25: Send Screen
- Part 24: Forgot Link Behaviour
- Part 23: Animating Controls (cont.)
- Part 22: Animating Controls
- Part 21: Hiding Control Groups
- Part 20: ModelState Errors (cont.)
- Part 19: ModelState Errors
- Part 18: Animating Info Pages (cont.)
- Part 17: Animating Info Pages
- Part 16: Keyboard Navigation
- Part 15: Accessing the DOM
- Part 14: All About the Username
- Part 13: CSRF Attacks
- Part 12: Http Requests
- Part 11: HMR
- Part 10: Color Inputs And Buttons
- Part 9: Animating Sub-Actions
- Part 8: Form Validation (cont.)
- Part 7: Form Validation
- Part 6: Form Group
- Part 5: Authenticator Validators
- Part 4: Authenticator Inputs
- Part 3: First Angular Component
- Part 2: Webpack
- Part 1: Beginning
You can view a completed example of the authenticator component here.
First Correction of a Correction
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.scss
-
authenticator
-
components
-
Source
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
}
}
}
}
Time for more dynamic css classes
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.pug
- authenticator.component.scss
- authenticator.component.ts
-
authenticator
-
components
-
Source
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}}
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;
}
}
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";
}
...
}
If everything has gone as planned then once we save our changes and refresh the browser our interface should go from (e) to (f).


Is valid or invalid enough
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.ts
-
authenticator
-
components
-
Source
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";
}
...
}


Finish up the input group
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.ts
-
authenticator
-
components
-
Source
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";
}
}
...
}
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.


Finish up the button icon
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.ts
-
authenticator
-
components
-
Source
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";
}
}
...
}
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.
