Skip to main content

The Website

Angular 2 Website Screenshot
Angular 2 has finally hit beta which was the mental milestone for me to start writing Angular 2 content. Hooray! To kick things off, I wanted to port a project that I covered in my Building a Website with AngularJS Pt 2 – Now With State! post to Angular 2. We are going to get acquainted with the brave new world of Angular 2 by building out a simple website so we can see how the Angular 2 pieces come together.
Grab the code from the repository below and let’s go!
Code

Compile and Serve

There are two main ways to compile and serve an Angular 2 application and that is via webpack or systemjs. For the sake of simplicity, we are going to go with systemjs and borrow from the Tour of Heroes repository which is covered in the official Angular 2 Tutorial.
You can write your Angular 2 application in ES5, EcmaScript 2015 or TypeScript, but the framework lends itself best to TypeScript. Writing in TypeScript requires a bit more setup but the return on investment is tenfold in terms of productivity and clarity in our code.
When you download the repository, the first thing you need to do is run npm i to install the package dependencies.

tsconfig.json

One of the package dependencies that we are going to install is typescript, which we will use to compile our TypeScript into the Type O negative version of JavaScript: the universal donor, ES5. We need to tell our compiler how we want to compile our TypeScript files, so we need to create a tsconfig.json file. The file below reads pretty well, but I will call out the two most important properties and those are “target”: “ES5” and “module”: “system”. We are setting our ECMAScript version to ES5 and indicating that we want to generate our modules in the system format.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "compilerOptions": {
    "target": "ES5",
    "module": "system",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  },
  "exclude": [
    "node_modules"
  ]
}

packages.json

We have defined how we want our TypeScript to be compiled, now we need to create a hook for the work to be done. In ourpackages.json file, we have defined a few tasks to watch our project, compile our source files and serve them up via a liteweight HTTP server. The task tsc:w calls our TypeScript compiler with the -w flag (which is shorthand for –watch) and the start task kicks off our compiler while spinning up our web server via npm run lite.
1
2
3
4
5
6
"scripts": {
  "tsc": "tsc",
  "tsc:w": "tsc -w",
  "lite": "lite-server",
  "start": "concurrent \"npm run tsc:w\" \"npm run lite\" "
}
We then call npm start when we are ready to compile and serve our application.

Bootstrapping

The first mystery of Angular 2 for me was “How in the world do I even run the app!?”. The second mystery was “Okay, so how do I bootstrap the application?!”.
The first thing we need to do to bootstrap our application is to include the necessary resources into our index.html file. Outside of the obvious Angular resources, the most important file at this stage of the game is system.src.js which is going to serve as our module loader.
1
2
3
4
5
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>
We are going to use systemjs via System to import our boot module.
1
2
3
4
5
<script>
    System.config({ packages: { app: { format: 'register', defaultExtension: 'js' } } });
    System.import('app/boot')
        .then(null, console.error.bind(console));
</script>
Inside the boot.ts file, we are importing three components; bootstrapROUTER_PROVIDERS and AppComponent. We then instantiate our application and specify our root level component, AppComponent and inject ROUTER_PROVIDERS as a submodule.
1
2
3
4
5
6
7
import {bootstrap} from 'angular2/platform/browser';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {AppComponent} from './app.component';

bootstrap(AppComponent, [
  ROUTER_PROVIDERS
]);
Back in our index.html file, our entry point in the markup happens at this line: <app>Loading…</app>. Angular has instantiatedAppComponent and loaded its template into the app element.
1
2
3
<body>
    <app>Loading...</app>
</body>
We have just covered how to compile, serve and bootstrap an Angular 2 application. Let us unpack what a component consists of so that we can make the connection between how AppComponent becomes app on our page.

The App Component

Angular components are really just JavaScript classes wrapped with love in Angular 2 metadata.
If you haven’t read my CIDER: My Checklist for Creating Angular 2 Components post, check it out. You will see the CIDER process in effect here.
The first thing I do when creating a component is to create the class. I will usually just stub it out because I will enhance it later.
1
export class AppComponent {}
The next thing we are going to do is to import our dependencies. In this case, we just need to import Component fromangular2/core.
1
import {Component} from 'angular2/core';
And from here, we are going to decorate our class by adding @component metadata to tell our application how we want theAppComponent to behave. We are defining the HTML element we want this class to target in the selector property as well as setting our template and styles via templateUrl and styleUrls, respectively. We are also injecting ROUTER_DIRECTIVES into our component at the directives property.
1
2
3
4
5
6
7
@Component({
  selector: 'app',
  templateUrl: 'app/app.component.html',
  styleUrls: ['app/app.component.css'],
  directives: [ROUTER_DIRECTIVES]
})
export class AppComponent {}
This is where things get a bit fluid in the CIDER process. I will often spend n iterations enhancing my components and this could go on indefinitely. In our case, we want to add in routing to our application and so we are going to import the appropriate modules and decorate our component with @RouteConfig. To enable routing, we are going to import RouteConfig and ROUTER_DIRECTIVESas well as AboutComponentExperimentsComponent and HomeComponent.
1
2
3
4
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {AboutComponent} from './about/about.component';
import {ExperimentsComponent} from './experiments/experiments.component';
import {HomeComponent} from './home/home.component';
We need the routing modules to enable routing and the additional components for our routing table. We are passing in an array of route definitions into @RouteConfig, which tell us the path of the route, the name of the route and what component is going to be mapped to that route. We want our home route to be our default route and so we will set that with useAsDefault: true. We will circle back around and talk about how the templates deal with routes in a moment but the basic gist of the new component router in Angular 2 is that each route maps to a component. Not really sure why they called it ComponentRouter though… JK!
1
2
3
4
5
@RouteConfig([
  {path: '/home',        name: 'Home',        component: HomeComponent, useAsDefault: true },
  {path: '/about',       name: 'About',       component: AboutComponent },
  {path: '/experiments', name: 'Experiments', component: ExperimentsComponent }
])
We are also going to import StateService and ExperimentsService into our component and then decorate our component on theproviders property which we will cover later. And I now present the #drumRoll AppComponent in its entirety!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {AboutComponent} from './about/about.component';
import {ExperimentsComponent} from './experiments/experiments.component';
import {HomeComponent} from './home/home.component';
import {StateService} from './common/state.service';
import {ExperimentsService} from './common/experiments.service';

@Component({
  selector: 'app',
  templateUrl: 'app/app.component.html',
  styleUrls: ['app/app.component.css'],
  directives: [ROUTER_DIRECTIVES],
  providers: [StateService, ExperimentsService],
})
@RouteConfig([
  {path: '/home',        name: 'Home',        component: HomeComponent, useAsDefault: true },
  {path: '/about',       name: 'About',       component: AboutComponent },
  {path: '/experiments', name: 'Experiments', component: ExperimentsComponent }
])
export class AppComponent {}
I really dig how everything up to this point has been self-documenting in its intentions and purpose. There isn’t a single piece in this component that I cannot immediately figure out what its purpose is.
To complete the CIDER process, we are going to repeat the process on a sub-component. All three of our components defined in our router are pretty much the same and so we are going to focus on the HomeComponent. There is some additional functionality in theExperimentComponent that we will cover in a later post.

The Home Component

We are going to break ground on our HomeComponent by defining the HomeComponent class. We are also defining and initializing two properties on our class for the component’s title and body. This is a website after all!
1
2
3
4
export class HomeComponent {
  title: string = 'Home Page';
  body:  string = 'This is the about home body';
}
We will import the appropriate dependencies.
1
import {Component} from 'angular2/core';
We will then decorate our class and set the selector and templateUrl properties.
1
2
3
4
@Component({
    selector: 'home',
    templateUrl: 'app/home/home.component.html'
})
We are going to use the StateService to store state between our routes and so we will add that to our component. Dependency injection within Angular 2 happens at the constructor and so we are going to add one to our class and inject the StateService. Components also have lifecycle hooks that we can use to sequence units of work. In our case, we want to retrieve and set ourmessage from StateService when our component is initialized. We will use the ngOnInit hook to make that call for us.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {Component} from 'angular2/core';
import {StateService} from '../common/state.service';

export class HomeComponent {
  title: string = 'Home Page';
  body:  string = 'This is the about home body';
  message: string;

constructor(private _StateService: StateService) { }

ngOnInit() {
    this.message = this._StateService.getMessage();
  }

updateMessage(m: string): void {
    this._StateService.setMessage(m);
  }
}
Again, incredibly self-documenting and easy to read.

The State Service

We are going to create the StateService class and then expose it as a provider to our application. I am not going to offer too much commentary on the code below because it is so rudimentary in nature. It is essentially a service that has a getter and a setter for amessage property.
1
2
3
4
5
6
7
8
9
10
11
export class StateService {
  private _message = 'Hello Message';

getMessage(): string {
    return this._message;
  };

setMessage(newMessage: string): void {
    this._message = newMessage;
  };
}
Things get interesting when we want to make StateService available for injection into other components. The first step is to importInjectable into our class.
1
import {Injectable} from 'angular2/core';
And then we decorate it with @Injectable().
1
2
3
4
5
6
7
8
9
10
11
12
@Injectable()
export class StateService {
  private _message = 'Hello Message';

getMessage(): string {
    return this._message;
  };

setMessage(newMessage: string): void {
    this._message = newMessage;
  };
}
We have spent most of our time working through building our components, so let us complete the loop by locking down our templates.

The Views

Using home.component.html as our reference point, let us take an abbreviated tour of the updated template syntax in Angular 2.
One-way data binding is defined exactly like it was in Angular 1.x through string interpolation.
1
2
3
<h1>{{title}}</h1>

{{body}}
User input events are no longer captured by adding custom Angular directives to our markup but rather through capturing native DOM events and wrapping them in parenthesis. We can see this in the code below as we capture the click event with(click)=”updateMessage(message)” and call updateMessage.
1
<button type="submit" class="btn" (click)="updateMessage(message)">Update Message</button>
Two-way data binding in Angular 2 is basically one-way data binding applied twice. We talked about binding using string interpolation but we can also bind to properties using brackets syntax. We combine property binding (component to view) and event binding (view to component) to accomplish two-way data binding. The solution is surprisingly simple as we wrap our ngModel in both brackets and parenthesis to make [(ngModel)]=”message”.
1
<input type="text" [(ngModel)]="message" placeholder="Message">
For context, here is the entire home template.
1
2
3
4
5
6
7
8
9
10
11
12
13
<h1>{{title}}</h1>

{{body}}

<hr>

<div>
    <h2 class="text-error">Home: {{message}}</h2>
    <form class="form-inline">
      <input type="text" [(ngModel)]="message" placeholder="Message">
      <button type="submit" class="btn" (click)="updateMessage(message)">Update Message</button>
    </form>
</div>

Routing Markup

We are back to where we started as we wrap up this lesson with a discussion on the template syntax for routing inapp.component.html. When we define a route, where does the component’s template actually get inserted? We set that insert point with router-outlet. It is the new ngView.
1
2
3
<div id="container">
    <router-outlet></router-outlet>
</div>
Great! So how do we navigate from one route to another? We do that with routerLink in the form of [routerLink]=”[‘/Home’]” as we can see in the code below.
1
2
3
4
5
6
7
8
9
<h1 id="logo">
  <a [routerLink]="['/Home']"></a>
</h1>

<div id="menu">
  <a [routerLink]="['/Home']" class="btn">Home</a>
  <a [routerLink]="['/About']" class="btn">About</a>
  <a [routerLink]="['/Experiments']" class="btn">Experiments</a>
</div>
And the entire app.component.html.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<header id="header">
  <h1 id="logo">
    <a [routerLink]="['/Home']"></a>
  </h1>

  <div id="menu">
    <a [routerLink]="['/Home']" class="btn">Home</a>
    <a [routerLink]="['/About']" class="btn">About</a>
    <a [routerLink]="['/Experiments']" class="btn">Experiments</a>
  </div>

  <div class="color"></div>
  <div class="clear"></div>
</header>

<div class="shadow"></div>

<div id="container">
  <router-outlet></router-outlet>
</div>

Review

Before you raise your billable rates by 15% and start selling this sweet Angular 2 website, let us do a quick review of what we covered.
  • We define how we want to compile TypeScript in our tsconfig.json file.
  • We use tsc -w and lite-server to compile and serve our application.
  • We learned how to use systemjs to handle module loading and bootstrapping our application
  • We saw how to use CIDER to build out our AppComponent and HomeComponent
  • We learned how to created an injectable service using @Injectable() metadata.
  • We did an abbreviated tour of the Angular 2 binding syntax.
  • We learned how routes are added to our view with router-outlet and the syntax for navigating to a route using routerLink
Let me know what you else you would like to learn in the comments below and in the meantime… #highFive!

Resources


Popular posts from this blog

How to read or extract text data from passport using python utility.

Hi ,  Lets get start with some utility which can be really helpful in extracting the text data from passport documents which can be images, pdf.  So instead of jumping to code directly lets understand the MRZ, & how it works basically. MRZ Parser :                 A machine-readable passport (MRP) is a machine-readable travel document (MRTD) with the data on the identity page encoded in optical character recognition format Most travel passports worldwide are MRPs.  It can have 2 lines or 3 lines of machine-readable data. This method allows to process MRZ written in accordance with ICAO Document 9303 (endorsed by the International Organization for Standardization and the International Electrotechnical Commission as ISO/IEC 7501-1)). Some applications will need to be able to scan such data of someway, so one of the easiest methods is to recognize it from an image file. I 'll show you how to retrieve the MRZ infor...

How to generate class diagrams pictures in a Django/Open-edX project from console

A class diagram in the Unified Modeling Language ( UML ) is a type of static structure diagram that describes the structure of a system by showing the system’s classes, their attributes, operations (or methods), and the relationships among objects. https://github.com/django-extensions/django-extensions Step 1:   Install django extensions Command:  pip install django-extensions Step 2:  Add to installed apps INSTALLED_APPS = ( ... 'django_extensions' , ... ) Step 3:  Install diagrams generators You have to choose between two diagram generators: Graphviz or Dotplus before using the command or you will get: python manage.py graph_models -a -o myapp_models.png Note:  I prefer to use   pydotplus   as it easier to install than Graphviz and its dependencies so we use   pip install pydotplus . Command:  pip install pydotplus Step 4:  Generate diagrams Now we have everything installed...

How to Remove course from Open-edX

Go to vagrant  => 1. In the edx-platform directory:  - cd /edx/app/edxapp/edx-platform 2. Run the following Django management command:   - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp lms dump_course_ids --settings aws    - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp lms dump_course_ids --settings=devstack 3. Find the course ID which you'd like to delete in the resulting list of course IDs. 4. Copy the course ID into the following command and run it:  - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp cms delete_course <COURSE_ID> --settings aws  -   sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp cms delete_course <COURSE_ID> --settings=devstack  - You'll be asked to verify the deletion . To verify the deletion, run the command from step 2 above and ensure that the course ID is not in the list. Help reference : ...