Advertisement

#3 Our Very First Angular Component

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.

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
    • webpack.config.js

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>
(a) Adding this call to RenderSection will allow us to add stylesheets to our views/pages.

main.site.ts

console.log("main.site.ts");
(b) We need to reduce the console log statements to avoid getting annoyed by them.

webpack.config.js (exports modified)

module.exports = {
    resolve: {
        extensions: [".ts", ".js"]
    },
    ...
}
(c) Adding the resolve property will avoid us receiving an error when importing typescript files inside other typescript files.

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).

Webpack error trying to import a typescript file without property setting the resolve property.
(d) Webpack error trying to import a typescript file without setting the resolve property.

Adding Scripts And Styles To Our Pages

  • WebUi
    • Pages
      • _SectionScripts.cshtml
      • _SectionStyles.cshtml

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>
(e) Partial view that we will use to render our conditional script tags.

_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>
(f)Partial view that we will use to render our conditional style tags.

Our First Razor Pages

  • WebUi
    • Pages
      • Account
        • Login.cshtml
        • Register.cshtml

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>
(g) The login page for our application located at /account/login.

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>
(h) The register page for our application located at /account/register.

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"
}
(i) New webpack entry point for creating our account-authenticator.bundle.js and account-authenticator.bundle.css files.
Advertisement

What about our typescript and scss files?

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

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");
(j) Temporary content of our account-authenticator.site.ts file for testing purposes.

account-authenticator.site.scss

.account-login {
    h1 {
        color: blue;
    }
}

.account-register {
    h1 {
        color: purple;
    }
}
(k) Temporary content of our account-authenticator.site.scss file for testing purposes.

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

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!
(l) The template for our authenticator component (Don't look now our old friend hello world is back).

authenticator.component.scss

h1 {
    color: fuchsia;
}
(m) The styles for our authenticator component.

authenticator.component.ts

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

@Component({
    selector: "authenticator",
    template: require("./authenticator.component.pug"),
    styles: [require("./authenticator.component.scss")]
})
export class AuthenticatorComponent {

}
(n) The logic for our authenticator component.

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);
(o) We create an authenticator module, which contains a reference to our authenticator component, and then bootstrap it.
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.
Angular selector not found error.
(p) The error that angular will throw if a component is bootstrapped without a corresponding selector being present within the 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.

Login page after bootstrapping our angular component.
(q) Result of bootstrapping our angular component in the login page.
Register page after bootstrapping our angular component.
(r) Result of bootstrapping our angular component in the register page.

Now that our component is up and running in the next video we will focus on doing something with it.

Exciton Interactive LLC
Advertisement