Advertisement

#25 Send Screen as a ViewChild

In this article we will create a child component of our authenticator that I will call the send screen. The send screen is our way of telling the user that we have sent a request to the server and we are now waiting for a response. We will also us it to be the first indication of whether or not the request was successful. In addition to creating the send screen we will also create the show and hide animations for it as well.

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

Adjusting the layout and styling

  • WebUi
    • Pages
      • Account
        • Login.cshtml
        • Register.cshtml
    • Source
      • sass
        • account-authenticator.site.scss

We will start by addressing the layout and styling of our login and register pages. As far as the layout goes we just need to remove the h1 tags so that we are left with the code shown in (a) and (b). And for styling I would like, at least for now, to have the authenticator centered on the screen (c).

Login.cshtml (1)

...
<div class="account-login">
    <authenticator></authenticator>
</div>
(a) The result of removing the h1 tag from the login page.

Register.cshtml (1)

...
<div class="account-register">
    <authenticator></authenticator>
</div>
(b) The result of removing the h1 tag from the register page.

account-authenticator.site.scss (1)

.account-login, .account-register {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
(c) Styling that we can use to center the authenticator on the screen.

Introducing the send screen

  • WebUi
    • Source
      • components
        • authenticator
          • send-screen.component.pug
          • send-screen.component.scss
          • send-screen.component.ts

The send screen is simply the element that slides down from the top of the authenticator and covers the inputs and info section when the user presses the send button. We will use it to display a message and change its coloring to indicate to the user whether or not a request was successful. Unlike the previous elements of the user interface we are going to create the send screen as a child component of our authenticator so we need to create typescript, scss, and pug files for it. To begin with we will just create a div with a css class, so that we can style it, in the template file (d). Next of course we will use that css class to apply some styling. By default we will position the send screen out of view to the top and make sure that it fills the entire height and width of the authenticator (e). Finally we need the typescript class so that we can add some behaviour to our send screen (f).

send-screen.component.pug (1)

div.send-screen
(d) Our starting template for the send screen.

send-screen.component.scss (1)

@import "../../sass/_bourbon-neat.scss";
@import "../../sass/2-base/_variables.scss";

.send-screen {
    transform: translate(0, -100%);
    @include position(absolute, 0 0 0 0);
    background-color: $light-gray;
}
(e) The starting styles for the send screen. The result of which is to have the height and width cover the entire authenticator component and for the send screen to begin off screen above our authenticator.

send-screen.component.ts (1)

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

@Component({
    selector: "send-screen",
    template: require("./send-screen.component.pug"),
    styles: [require("./send-screen.component.scss")],
    providers: []
})
export class SendScreenComponent {

}
(f) The bare bones class so that we can include the send screen within our authenticator.

Including the send screen

  • WebUi
    • Source
      • app
        • account-authenticator.site.ts

At the most basic level we just need to include the 'send-screen' selector within the template of our authenticator and angular will take care of injecting the send screen template for us. In order for angular to do this though we have to include a reference to the send screen component within our module (g). With that done like I said we just need to include the selector in the authenticator component's template (h). We also need to make a small change to the styling of the authenticator (i) so that the send screen is positioned relative to the authenticator and not the window.

account-authenticator.site.ts (1)

...
import { SendScreenComponent } from "../components/authenticator/send-screen.component";

@NgModule({
    ...
    declarations: [..., SendScreenComponent],
    ...
})
export class AuthenticatorModule { }
(g) In order for angular to inject our component where it is needed we need to add a reference to it within our module.

authenticator.component.pug (1)

...
div.authenticator
    ...
    send-screen
(h) To have the send screen's template included within the authenticator ui we just need to include the 'send-screen' selector within the authenticator template.

authenticator.component.scss (1)

...
.authenticator {
    ...
    position: relative;
    ...
}
(i) If we do not change the position property of our authenticator the send screen will be positioned relative to the window which is not what we want.

With those changes made and once webpack has injected the changes we can see, with the help of the developer tools, that the send screen is being position at the correct location and height and width appear to be correct (j).

Using the developer tools we can see that the send screen is positioned just above the
            authenticator ui and appears to have the correct width and height.
(j) Using the developer tools we can see that the send screen is positioned just above the authenticator ui and appears to have the correct width and height.
Advertisement

How about another first...a view child

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

Angular provides several different ways for components to interact between themselves which you can read more about here. The approach we are going to take is to use a concept called a ViewChild. To define a view child we first need to import the 'ViewChild' decorator from the angular core package and use it to decorate a private field within the parent, in this case ths authenticator, component (k). With that done when we save our changes and return to the browser we will see in the console that the _sendScreen is set to undefined (l). Since we initially set it to null we know that angular has attempted to set it the the send screen component but was unable to do so. Fortunately again the fix for this is very simple. We just need to include a template variable so that angular is able to match up the send screen component with our field (m). It is of course not a coincidence that the template variable denoted with the hashtag within the template is the same as the string passed into the ViewChild decorator. When we include the variable we can now see that we do in fact have a reference to the send screen component within our authenticator component (n).

authenticator.component.ts (1)

...
import { ..., ViewChild } from "@angular/core";
...
import { SendScreenComponent } from "./send-screen.component";
...
export class AuthenticatorComponent ... {
    ...
    @ViewChild("sendScreen") private readonly _sendScreen: SendScreenComponent = null;
    ...
    public ngAfterViewInit(): void {
        ...
        console.log(this._sendScreen);
    }
    ...
}
(k) Telling angular that we want a reference to a child component is a simple matter of just importing the ViewChild decorator from the angular core package and applying it to a field within the parent.

authenticator.component.pug (2)

...
div.authenticator
    ...
    send-screen(#sendScreen="")
(m) In order for angular to know what child we are talking about we need to include a template variable with the same name.
Once we have implemented the required code for a view child within
            our component when angular attempts to bind it to our field we find that it is undefined.
(l) Once we have implemented the required code for a view child within our component when angular attempts to bind it to our field we find that it is undefined.
(n) In order for angular to know what child we are attempting to bind to our field we need to add a template variable with the same name within our template.

Now that we know that we have a reference to the send screen component don't forget to remove the console.log(this._sendScreen); statement.

Once again we need a reference to a DOM element

  • WebUi
    • Source
      • components
        • authenticator
          • send-screen.component.pug
          • send-screen.component.ts

From our past experience we know that we need a reference to a DOM element in order to animate it. We also know how we go about getting one. We will start by applying an id to our send container (o). We will of course define that id within our send screen component (p). We also need to inject an instance of the ElementRef and DOMReaderService classes into our constructor.

send-screen.component.pug (2)

div.send-screen([attr.id]="sendScreenId")
(o) We need to add an id to our container so that we can reference it within our component so that we can animate it.

send-screen.component.ts (2)

import { ..., ElementRef, AfterViewInit } from "@angular/core";
import { DOMReaderService } from "../../services/dom-reader.service";
...
export class SendScreenComponent implements AfterViewInit {
    private _container: HTMLElement = null;

    public get sendScreenId() { return "assp-s-cont"; }

    constructor(private readonly _elementRef: ElementRef, private readonly _domReader: DOMReaderService) { }

    public ngAfterViewInit(): void {
        this._container = this._domReader.findChildById(this._elementRef.nativeElement, this.sendScreenId);
        console.log(this._container);
    }
}
(p) With the id being defined and applied to our template we can use the ElementRef and DOMReaderService classes to get a reference to the container DOM element.

Once again checking the console we can see that we do have a reference to the container DOM element (q) .

Console output showing the result of using the dom reader service to find
            the send container element.
(q) Console output showing the result of using the dom reader service to find the send container element.

As usual now that we are sure this is working don't forget to remove console.log(this._container); statement.

Showing and hiding the send screen

  • WebUi
    • Source
      • components
        • authenticator
          • authenticator.component.ts
          • send-screen.component.ts

By now we are old pros at animating things in and out of view. We begin this time by, as usual, creating a field that will hold the animation timing and specifying show and hide methods (r). Now that we have the ability to show and hide the send screen we just need to adjust our authenticator component so that it calls the show method when a request is sent to the server and hides it when a response is received (s).

send-screen.component.ts (3)

...
export class SendScreenComponent ... {
    private readonly _showHideTiming: AnimationEffectTiming = {
        duration: 750,
        easing: "ease-in-out",
        fill: "forwards"
    }
    ...
    public hide = () => {
        this._container.animate({
            transform: ["translate(0, 0)", "translate(0, -100%)"]
        }, this._showHideTiming);
    }

    public show = () => {
        this._container.animate({
            transform: ["translate(0, -100%)", "translate(0, 0)"]
        }, this._showHideTiming);
    }
}
(r) As we have done in the past we are specifying the animation timing as a field so we can use it in both the show and hide animations.

authenticator.component.ts (2)

...
export class AuthenticatorComponent ... {
    ...
    public isButtonDisabled = (key: string) => {
        switch (key) {
            //case this.buttonSendKey:
            //    return this.authForm.invalid;
            default:
                return false;
        }
    }
    ...
    private login = () => {
        ...
        this._authenticatorHttpService.login(...,
            () => {

            },
            () => {
                ...
                this._sendScreen.hide();
            });
    }
    ...
    private onClickSend = () => {
        this._sendScreen.show();
        ...
    }
    ...
}
(s) To make it easier to test we will allow the send button to be pressed even if the form is invalid. We are also going to show the send screen when the user attempts to login and hide it if the server response is an error.

Now when we press the send button we are shown an exceptionally designed gray screen that animates in from the top and once a response is received animates back out (t).

When the send button is pressed the send screen is animated in from the top
            and once a response is received is animated back out.
(t) When the send button is pressed the send screen is animated in from the top and once a response is received is animated back out.
Exciton Interactive LLC
Advertisement