Creating the boilerplate components
Contents
Creating a Component: Since we added the Angular 2 Snippets extension, creating an empty component is done in seconds. To begin with, we create the app.component.ts
file in the app
folder of our project. Right-click on the app
folder in the VSCode ‘Explorer’ pane and select New file
. In the empty editor pane, just enter the two characters ‘a2’ and a selection menu will pop up which offers a2component.
When we select it, ready-to-use boilerplate code will be inserted.
import { Component, OnInit } from '@angular/core'; @Component({ moduleId: module.id, selector: 'selector', templateUrl: 'fileName.component.html' }) export class ComponentNameComponent implements OnInit { constructor() { } ngOnInit() { } }
We don’t need the module.id
and the empty ngOnInit()
implementation, so we delete it. Our only component element in index.html
is called my-app
, so we change the selector
to it. Because we want to embedd the HTML template right here, we change templateUrl
to template
. and put a title placeholder into it using the Angular 2 template language. Finally, our app.component.ts
file must look like this:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'my-app', template: '{{title}}' }) export class AppComponent { title: String; constructor() { this.title = "My First Angular 2 App" } }
Next we create the Angular 2 module. The name we specified in index.html
was app.module.js
(Javascript), so we need to create a corresponding TypeScript file app.module.ts,
just like we did with the app.component.ts
. Again, in the empty file editor pane we enter the two characters ‘a2’. This time we select a2appModule
. After the boilerplate code has been generated, we change the file so it looks like this:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent ], bootstrap: [ AppComponent ] }) export class AppModule { }
What’s left do do is to create our bootstrap code. As a seperation of concern, we put it into a seperate file. This makes it easier to write test code for the modules and components because the platform dependent bootstrap code is out of the way:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; const platform = platformBrowserDynamic(); platform.bootstrapModule(AppModule);
Browser Sync: As already mentioned, we will find ourselves repeatedly go through a code change / check results loop. This normally requires the files of our web app to be copied to a web server and then loaded into the browser over and over again. We can minimize these manual steps by using the build tools we put into the devDependencies
section. In order to make use of these tools, take a look at the scripts
element in package.json
:
"scripts": { "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ", "lite": "lite-server", "postinstall": "typings install", "tsc": "tsc", "tsc:w": "tsc -w", "typings": "typings" }
We will find ourselves in deep trouble if we ignore the postinstall line in the scripts section above. We must not forget that this line is only executed if we installed the project’s dependencies by entering npm install
, which we didn’t. Remember, we manually installed all needed libraries so there was no need to bulk install everything again. This postinstall
makes the difference, so we need to manually run the command behind it before we start anything else. In the terminal pane enter:
typings install
If everything works as expected, we can now enter the following command into the terminal pane:
npm start
What we will see is that a browser window is opened and our app’s index.html
page is on display. Now, each time we save a file, the browser will automatically refresh the page, reloading all files specified in index.html depending on whether they have changed (status 200) or not (status 304). Don’t forget to enter the start command again after you restarted VSCode. Because the terminal pane is now blocked by the background process which we started, we can open a second terminal pane by entering <Shift>+<Ctrl>+ö
.
Note: If the npm start
command fails, make sure you didnn’t make any typos in the code because, if the initial compilation (transpilation 🙂 fails, the command fails completely. Only if it compiles successfully, BrowserSync will become active. If you wanna know why, just take a look at the start
script in package.json
:
"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
Workspace settings: Taking a look into the explorer pane on the left, we notice that there are now quite a few files more than we’ve created ourselves. This happens because the automatic Typescript transpiling process which we initiated when we entered npm start,
automatically transpiles our Typescript source files into Javascript and corresponding map files. To supress the display of those files, we can create a VSCode workspace setting file. We press <Shift>+<Ctrl>+p
and enter ‘setting’. Then we can select the topmost entry ‘Preferences: Open Workspace Settings’ and put the following content in there:
{ "typescript.tsdk": "C:\\Development\\workspaces\\VisualStudio-Code\\MyFirstAngular2Project\\node_modules\\typescript\\lib", "files.exclude": { "**/.git": true, "**/.svn": true, "**/.hg": true, "**/.DS_Store": true, "**/*.js": { "when": "$(basename).ts" }, "**/*.map": true }
It’s time to get into real development business. Our next refinement of the example code will make use of the master / detail pattern, using a simple customer data array to create a list of customers. When we have the list, we add code to show customer details when an entry in this list is clicked. The UI will be a simple two column layout, with the customer list going into the left column and the details into the right one.
We start with the most simple HTML layout, because the master and details HTML template need to be in the same component because otherwise we had to provide much more intelligence for communication between components:
<body> <my-app>Loading...</my-app> <master-detail></master-detail> </body>
Now that the new <master-detail>
component element is in place , we can make use of it in code. First we will create an Angular2 component for the customer list. In the project’s app folder, we create a file called cust-list.component.ts
and link it to the <master-details>
component element in index.html
by using the selector
property:
import { Component } from '@angular/core'; import { Customer } from './customer'; @Component({ selector: 'master-detail', template: ` <div id="left"> <table class="table"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Firstname</th> </tr> </thead> <tbody> <tr *ngFor="let customery of customers" class="row" [class.selected]="customery === selectedCustomer" (click)="onSelect(customery)"> <td>{{customery.id}}</td><td>{{customery.name}}</td>{{customery.firstname}} </tr> </tbody> </table> </div> <div id="right"> <customer-detail [customer]="selectedCustomer">Loading...</customer-detail> </div> ` }) export class CustListComponent { customers = CUSTOMERS; selectedCustomer: Customer; onSelect(customer: Customer): void { this.selectedCustomer = customer; } } const CUSTOMERS = [ { "id": 1, "name": "Houston", "firstname": "John", "street": "Bakerstreet 56", "zipcode": 45332, "city": "New York", "country": "United States", "phone": "+1 456 34342930", "email": "jhouston@yahoo.com", "age": "67" }, { "id": 2, "name": "Smith", "firstname": "Ken", "street": "Wakersfield Road 22", "zipcode": 455344, "city": "Boston", "country": "United States", "phone": "+1 665 9457734", "email": "ksmith@google.com", "age": "38" } ]
Let’s walk through the above code step by step:
Line 1: Import of the required Angular 2 component class
Line 2: A yet to be created Customer class, because we will later use this class also
Line 4: The @Component
decorator which provide the required metadata for Angular
Line 5: The selector property is our link to the corresponding HTML element which will hold our component
Line 6 – 26: The template for what we want to put into the component
Line 17: The Angular “repeater” directive. It marks that <tr>
element (and its children) as the “repeater” template
Line 18: The row’s data template. This is where the customer data will repeadetly be replacing the placeholders. Angular evaluates all expressions in double curly braces, and converts the expression results to strings.
Line 24: Here we introduce a new component <customer-details>
. We provide the corresponding Angular 2 <customer-detail>
component with the currently selected customer
object.
What’s left to do is to create the Customer class and the detail component. We create the Customer class file customer.ts
in the app folder:
export class Customer { id: number; name: string; firstname: string; street: string; zipcode: number; city: string; country: string; phone: string; email: string; age: number; }
Finally, here is the details component which links to the <customer-details>
element in the list component. The name is cust-detail.component.ts
:
import { Component, Input } from '@angular/core'; import { Customer } from './customer'; @Component({ selector: 'customer-detail', template: ` <div *ngIf="customer"> <div><label>id: </label>{{customer.id}}</div> <div> <label>name: </label> <span>{{customer.name}}</span> </div> <div> <label>email: </label> <span>{{customer.email}}</span> </div> </div> ` }) export class CustDetailComponent { @Input() customer: Customer; }
For now, we put the necessary CSS rules into the <head>
section of index.html
:
<style> #left {width: 50%; float: left;} #right {margin-left: 50%;} body { color: #444; font-family: Arial, Helvetica, sans-serif; font-weight: lighter; } .selected, .row:hover { color: #607D8B; background-color: #DDD; left: .1em; } </style>
The customer data is currently hard coded in the list component. We will put our data source on a more professional basis in the next chapter. If you want to see a more elaborate version of a customer master/detail app, see http://plnkr.co/edit/aEjlwIKKhcErWAnIhY3C?p=preview.
In order to link everything together what we have added to the customer app, we need to extend our Angular 2 module. This is how it should look by now:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { CustListComponent } from './cust-list.component'; import { CustDetailComponent } from './cust-detail.component'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, CustListComponent, CustDetailComponent ], bootstrap: [ AppComponent, CustListComponent ] }) export class AppModule { }
Play with it before continuing with the next chapter.