#25 Send Screen as a ViewChild
Thursday, March 22, 2018
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.
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.
Adjusting the layout and styling
-
WebUi
-
Pages
-
Account
- Login.cshtml
- Register.cshtml
-
Account
-
Source
-
sass
- account-authenticator.site.scss
-
sass
-
Pages
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>
Register.cshtml (1)
...
<div class="account-register">
<authenticator></authenticator>
</div>
account-authenticator.site.scss (1)
.account-login, .account-register {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
}
Introducing the send screen
-
WebUi
-
Source
-
components
-
authenticator
- send-screen.component.pug
- send-screen.component.scss
- send-screen.component.ts
-
authenticator
-
components
-
Source
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
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;
}
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 {
}
Including the send screen
-
WebUi
-
Source
-
app
- account-authenticator.site.ts
-
app
-
Source
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 { }
authenticator.component.pug (1)
...
div.authenticator
...
send-screen
authenticator.component.scss (1)
...
.authenticator {
...
position: relative;
...
}
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).
How about another first...a view child
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.ts
-
authenticator
-
components
-
Source
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);
}
...
}
authenticator.component.pug (2)
...
div.authenticator
...
send-screen(#sendScreen="")
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
-
authenticator
-
components
-
Source
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")
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);
}
}
Once again checking the console we can see that we do have a reference to the container DOM element (q) .
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
-
authenticator
-
components
-
Source
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);
}
}
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();
...
}
...
}
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).