#3 Our Very First Angular Component
Friday, October 20, 2017
In this article we will begin the effort to allow users to register and login to our application. We begin by creating our first Angular component. And once created we will configure a corresponding module that will allow Angular to bootstrap our component and display it in our browser window.
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
Modifying Our Existing Code
From this point forward I will just assume that you have the web server running and the application is being displayed in your browser. To remind you
how to do this if you are using Visual Studio you go to the Debug menu and select Start Without Debugging or using Ctrl+F5. If you are not using Visual
Studio you need to navigate a console to the root of the project and use the command dotnet run
and then
navigating your browser to the address displayed in the console. The console method will work for now but as the application gets more complex we will
need iisexpress running so you will need to start the web server yourself at that point if not using Visual Studio.
I will also assume that you have you have at least one console window open at the root of our project.
-
WebUi
-
Pages
- _Layout.cshtml
-
Source
-
app
- main.site.ts
-
app
- webpack.config.js
-
Pages
Our first order of business is to modify a couple of files that we have already dealt with in our previous articles. First we need to modify our _Layout.cshtml by adding the statement shown in (a) which will allow us to add stylesheets to our razor pages or views if we choose. Next we will modify our main.site.ts file so that it contains just one console statement (b) instead of the three. This way we can be sure the bundle is still working correctly and not get overly annoyed by the spam from it in the browser console. Once that is done we can rebuild our bundles and check the console to make sure our changes have worked. The last file that we need to modify at the moment is our webpack.config.js file by adding a resolve property (c) to the exported configuration object (For the record I'm not counting this as a mistake since we didn't need it until now even though I might have forgotten it in the last video).
_Layout.cshtml
<head>
...
@RenderSection("Styles", required: false)
</head>
main.site.ts
console.log("main.site.ts");
webpack.config.js (exports modified)
module.exports = {
resolve: {
extensions: [".ts", ".js"]
},
...
}
If we attempt to create a bundle which imports another typescript file without first setting the resolve property we will get an error of the type shown in (d).
Adding Scripts And Styles To Our Pages
-
WebUi
-
Pages
- _SectionScripts.cshtml
- _SectionStyles.cshtml
-
Pages
Next we will add a couple of partial views to our project to make it as simple as possible for us to add references to our scripts and styles within our pages. We will create both files within our Pages folder with one named '_SectionScripts.cshtml' (e) and the other '_SectionStyles.cshtml' (f) (I use the convention of placing an underscore before the name of a view/page to indicate that this is a partial view). Asp.Net Core gave us the concept of a tag helper which are tags that we place in our razor views/pages that look like html tags but will execute c# code when they are rendered. One of the tag helpers that is provided by Microsoft is the environment tag. This tag will conditionally add its contents to the page depending on the environment we are running i.e. development or production. As was mentioned previously most of our pages will import one script and one stylesheet and the exact name of these files will depend on what environment we are running. If we are developing then we will want the un-minified versions and of course if it is a production build we will want the minified versions.
In both (e) and (f) we are defining a development and production path to the corresponding bundle. The paths are relative to the content root of our application which is set to the wwwroot folder. In both we will expect to pass in the name of the bundle and then depending on the environment the appropriate script/style bundle will be fetched for our page.
_SectionScripts.cshtml
@model string
@{
var developmentBundle = $"/js/{Model}.bundle.js";
var productionBundle = $"/js/{Model}.bundle.min.js";
}
<environment include="Development">
<script src="@developmentBundle"></script>
</environment>
<environment exclude="Development">
<script src="@productionBundle" asp-append-version="true"></script>
</environment>
_SectionStyles.cshtml
@model string
@{
var developmentBundle = $"/css/{Model}.bundle.css";
var productionBundle = $"/css/{Model}.bundle.min.css";
}
<environment include="Development">
<link rel="stylesheet" href="@developmentBundle" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="@productionBundle" asp-append-version="true" />
</environment>
Our First Razor Pages
-
WebUi
-
Pages
-
Account
- Login.cshtml
- Register.cshtml
-
Account
-
Pages
Our first goal is to allow people to login/register with our application and the very first small step towards that goal is to create a folder within our
Pages folder name 'Account'. Inside of this folder we will create two razor pages, one named 'Login.cshtml' (g)
and the other 'Register.cshtml' (h). By convention
the url for these pages will then be /account/login and /account/register respectively. In both of these pages we will define a title, which is displayed
in the tab of the web browser, and we will add the script and style references to our page through the section declarations. These references are placed in
the page where the corresponding calls to @RenderSection
are made within our _Layout.cshtml.
If we navigate our browser to these pages we will see in our console that there are requests for css and javascript bundles that are returning 404 since they do not exist yet. In the next section we will modify our webpack.config.js to correct this problem.
We are going to cheat a little by adding the authenticator tag to both pages. We will see what this tag is for towards the end of the article.
Login.cshtml
@page
@model WebUi.Pages.Account.LoginModel
@{
ViewBag.Title = "Account Login";
}
@section Styles
{
@Html.Partial("_SectionStyles", "account-authenticator")
}
@section Scripts {
@Html.Partial("_SectionScripts", "account-authenticator")
}
<div class="account-login">
<h1>Login</h1>
<authenticator></authenticator>
</div>
Register.cshtml
@page
@model WebUi.Pages.Account.RegisterModel
@{
ViewBag.Title = "Account Register";
}
@section Styles
{
@Html.Partial("_SectionStyles", "account-authenticator")
}
@section Scripts {
@Html.Partial("_SectionScripts", "account-authenticator")
}
<div class="account-register">
<h1>Register</h1>
<authenticator></authenticator>
</div>
Do Your Thing Webpack
-
WebUi
- webpack.config.js
In (g) and in (h) we are referencing a bundle name 'account-authenticator' in both our scripts and styles sections
which, as was just mentioned, do not exist yet. The way we will correct this is by returning to our webpack.config.js file and adding a new entry point
(i). As a reminder the result of invoking the siteScriptsAndStyles
function with
the argument of 'account-authenticator' is an array with two values, one pointing to the relative path of the typescript file and the other to the relative path of the scss file
both with the name 'account-authenticator.site' with the appropriate extension. In the next section we will actually create our entry point files.
webpack.config.js (entry modified)
const entry = {
"account-authenticator": siteScriptsAndStyles("account-authenticator"),
"main": siteScriptsAndStyles("main"),
"vendor": "./Source/app/vendor.ts"
}
What about our typescript and scss files?
-
WebUi
-
Source
-
app
- account-authenticator.site.ts
-
sass
- account-authenticator.site.scss
-
app
-
Source
We have specified the new entry point for webpack but if we tried to create our bundles the build would fail since we have not yet created
the corresponding typescript and scss files yet. We will correct this by creating the typescript file in our app folder,
'account-authenticator.site.ts' (j),
and the scss file in our sass folder, 'account-authenticator.site.scss' (k). Once we have added these files it is
time to rebuild our bundle
using the npm run build
command again. And once we have refreshed the pages we should see the 'account-authenticator'
statement in the console for each page. Both (j) and (k) only contain
temporary code and in the case of the scss file our beginning attempt at creating the ugliest color combination possible.
account-authenticator.site.ts
console.log("account-authenticator");
account-authenticator.site.scss
.account-login {
h1 {
color: blue;
}
}
.account-register {
h1 {
color: purple;
}
}
With our new entry point files created we can rebuild our bundles again. This time though instead of running the build command we will instead use the watch command. So in
the console we need to enter npm run watch
which will rebuild the bundles and will begin watching our files for changes. Once the bundles
are built we just need to refresh our pages to see that we are no longer receiving any 404 error codes and we should also see 'account-authenticator' displayed in the
console.
Most of the time while I am developing a web application I have the browser's developer tools open and on important setting that you will want to make is to disable the browser's caching ability. In Chrome there is a little checkbox located at the top of the Network tab labeled 'Disable cache'.
But We Want To Do More Than Print Messages To The Console
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.pug
- authenticator.component.scss
- authenticator.component.ts
-
authenticator
-
components
-
Source
By now we have become pretty good at printing statements in the console of our browser but it's time that we stepped up our game and start printing our statements in the browser window itself. We are going to accomplish this lofty goal by creating our first angular component. To try to keep our project as orderly as possible we are going to create a new folder in our Source folder named 'components'. This folder will contain all of the angular components that we create. Next we need to create an 'authenticator' folder inside our components folder. Now we need to create three files inside our newly created authenticator folder: 'authenticator.component.pug' (l), 'authenticator.component.scss' (m), and 'authenticator.component.ts' (n). As we have already discussed the pug file will contain the template, the scss file will contain the styles, and the ts file will contain the logic of our component.
authenticator.component.pug
h1 Hello World!
authenticator.component.scss
h1 {
color: fuchsia;
}
authenticator.component.ts
import { Component } from "@angular/core";
@Component({
selector: "authenticator",
template: require("./authenticator.component.pug"),
styles: [require("./authenticator.component.scss")]
})
export class AuthenticatorComponent {
}
One thing to notice is the selector: "authenticator"
statement in the component metadata statement in (n).
If you recall we included a
non-standard tag <authenticator></authenticator>
in both our login (g) and
register (h) pages. When we bootstrap our application angular will look for a tag in our page that matches the specified selector. But first
things first we need to bootstrap a module containing our authenticator component and we do just that by modifying the account-authenticator.site.ts file
(o).
account-authenticator.site.ts (modified)
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AuthenticatorComponent } from "../components/authenticator/authenticator.component";
@NgModule({
imports: [
BrowserModule
],
declarations: [AuthenticatorComponent],
bootstrap: [AuthenticatorComponent]
})
export class AuthenticatorModule { }
const platform = platformBrowserDynamic();
platform.bootstrapModule(AuthenticatorModule);
An important note is angular will only look for a specific selector once. If it is not found angular will throw and error (p) in the console and if it is found will stop looking. As a result we can have only one component with a particular selector per page.
Since we are running the watch command all we need to do in order to see our changes is to refresh our pages and we should see the result of our world class design abilities in our login (q) and register (r) pages. The login and register text in their respective pages are set within the page itself and the 'Hello World!' statement is coming from the template of our angular component.
Now that our component is up and running in the next video we will focus on doing something with it.