All the examples in this blog post are using
TypeScript
, sinceAngular 2
recommends to use it. We will try to keep this post as up to date as possible.
In order to get started please follow the quickstart example provided in the official angular docs, as it's up to date with all the things you need.
Building a Top-Level Component
Every Angular app has at least one root component, conventionally named
AppComponent
, that hosts the client user experience. Components are the basic building blocks of Angular applications. A component controls a portion of the screen — a view — through its associated template.
Import the Component Object
import { Component } from '@angular/core'
Angular 2 make use of the ES2015 module syntax (aka ES6). For those unfamiliar with the syntax, it makes use of import
statements to access different pieces of code. In addition to these import
statements, this syntax also relies upon export
statements to make code accessible to the rest of our application.
When working with Angular 2, we will see these import
statements being used to gain access to core features of the framework through different Angular 2 libraries. In the code block we just looked at, we see the import
statement telling Angular that we want to access the Component
decorator from the @angular/core
library, which is one of the main libraries that this framework uses.
Some of the other APIs that are central to developing web applications in Angular 2.x. are:
- @angular/http - For making HTTP requests
- @angular/common - Common core things including form validation, NgIf, NgSwitch and more.
- @angular/router - The Router is responsible for mapping URLs to components
Inside of each API you can find the following types to import from:
Add Meta-data to our Component using TypeScript Decorators
Once the Component
object is imported, we can then begin describing our component using TypeScript's @ symbol. By checking out Angular 2's API guide, we can see that decorators are used to create new instances of @Directive
, @Injectable
, @RouterConfig
and more.
Everytime Angular sees a decorator(@
), it will know that we want to create a new instance of a component, and it will create our component according to our configuration meta-data.
We are able to configure the following options through our component's meta-data:
-
selector - defines the name of the HTML tag where this component will live. In this case, our component will by shown through the
<my-app></my-app>
tags. -
providers - This is where we pass in any services that that want a component to access. If you want a global service, you wont pass it here, but in the Application's bootstrap. It will then be available throughout the all application, like the following example:
// main entry point
import { bootstrap } from '@angular/platform-browser-dynamic';
import { HTTP_PROVIDERS } from '@angular/http';
import { BaseService } from './base.service'; // <- global service import
import { AppComponent } from './app.component';
bootstrap(AppComponent, [
BaseService, // <- global service injection
HTTP_PROVIDERS
])
.catch(err => console.error(err));
-
directives - We use the directive option when we want to access another component directive. Because this is the top-level component, we often see components being passed into this option.
-
styles - The styles option is used to style a specific component. One of the advantages of using components is their ability to encapsulate their styles. We could have just as easily linked to an external stylesheet by using the styleUrls property. An array of stylesheets can be passed into this option.
-
template - This is the portion of our component that holds our template. It is an integral part of the component as it allows us to tie logic from our component directly to a view. When defining a template, we can either write it inline, or we can opt to use
templateUrl
to link to an external template.
Export the Component
The exported class is where we can define any variables or functions that our component's template will be using:
// app/app.ts
export class AppComponent {
componentName:string = 'AppComponent';
}
Angular 2 makes use of the ES2015 module syntax. By using the export
statement, this component can be imported into different files in a project so it is a pivotal part of Angular 2.
Bootstrap our Component
We can use inline HTML and CSS:
// app/app.ts
import { Component } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';
@Component({
selector: 'my-app',
styles: [`
h1 {
color:#545454;
background:#02A8F4;
padding:15px;
box-shadow:2px 2px 2px 0 rgba(0, 0, 0, 0.3);
}
`],
template: `
<h1>Hello from the {{componentName}}.</h1>
`
})
export class AppComponent {
componentName:string = 'AppComponent';
}
bootstrap(AppComponent);
Or we can import our template and css file(s):
// app/app.ts
import { Component } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';
@Component({
selector: 'my-app',
styleUrls: ['main.css', 'other.css'],
templateUrl: 'main.html'
})
export class AppComponent {
componentName:string = 'AppComponent';
}
bootstrap(AppComponent);
Nesting another Component
After creating a top-level component, we will now create a new component, so it can be imported by other component's.
We will look into:
- Use Angular 2's built-in directives (not to be confused with Components)
- Use a service to get data
- Loop over that data in our template
- look at Angular 2's new template syntax
- basics of Dependency Injection and services
First, tell Angular that you want this component to live inside of <my-weapons></my-weapons>
. The template attached to this component is going to use the ngFor
structural directive
to iterate over a list of names.
The ngFor
directive is the successor to ng-repeat
from Angular 1.x
. In addition to ngFor
, Angular 2
provides developers with a handful of other camel-case directives.
In addition to the ngFor
statement, we also create another componentName variable just like in the AppComponent
component. You can use the same variable name and not have to worry about it messing with other components that utilize the same naming conventions.
// app/weapon.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-weapons'
template: `
<h1>Hello from the {{ componentName }}!</h1>
<div *ngFor="let w of weapons">
<h3>Name: {{ w.name }}</h3>
<h4>Type: {{ w.type }}</h4>
</div>
`
})
Whenever we see an asterisk (*
) prepended onto a directive, we immediately know that this will be using template tags (<template></template>
) to render this piece of code.
This ability to use symbols to create dynamic behavior is what Angular calls "syntactical sugar". It makes the process of developing web applications a quicker process by cutting out bits of unnecessary code without cluttering up our template.
Angular 2 Syntax overview
The local variables #
The pound symbol #
is used to declare a local variable in our templates. When working with templates in Angular 2, we can use this symbol to reference different DOM elements in our application. These local variables will act as references to specific DOM elements. Moreover, they provide us with the ability to do things like pull values from input fields and add dynamic functionality to a DOM element.
<a #weaponName>Click to shoot weapon</a>
Event Bindings ()
In Angular 1.x, we had to rely on directives like ng-click
, ng-blur
, ng-mouseenter
, to handle different events. Angular 2 has dropped all of these directives and implemented a new, powerful syntax that allows developers to bind to any valid DOM event. For example:
By passing in the name of the event into parenthesis, which signifies an event binding in Angular 2. When working with events in Angular, it's important to know that events flow out of the template to the component.
<a (click)="shootWeapon()">Click to shoot weapon</a>
Similar to Angular 1.x, you can still pass the $event
reference if you want to grab it inside your function. Just add it to the function call like so:
<a (click)="shootWeapon($event)">Click to shoot weapon</a>
Property Binding []
On the opposite side of event bindings ()
lie Angular's square-bracket syntax []
which signify a property binding.
When working with property bindings, any values flow from the component class to the template, whereas event bindings work vice-versa.
When working with Angular 1.x, we had to use directives like ng-src
and ng-href
to dynamically pass in values to DOM elements, however the use of property bindings allows us to drop the ng-
prefix, and simply pass in the name of the property we wish to define.
<img [src]="urlToImage">
The use of property bindings is particularly key when define attribute directives.
<div [style.color]="color"></div>
WeaponsComponent Class to use @Injectable()
We access data by using services and some times these services make use of another feature of Angular 2 Dependency Injection and the use of @Injectable
. Let's create our weapons object in a file located at app/weapons.service.ts
// app/weapon.service.ts
// 1. Import Injectable Decorator
import { Injectable } from '@angular/core';
// 2. Use @Injectable() to declare the WeaponService class as an Injectable
@Injectable()
/**
3.1. Create and export WeaponService Class { }
3.2. create weapons object and declare it to be an Array of any values/
3.3. Add weapons object to the constructor function
3.4. create getWeapons() function to call all weapon values.
**/
// 3.1.
export class WeaponService {
// 3.2
weapons:Array<any>;
// 3.3
constructor() {
this.weapons = [
{ "name": "Yang's Recurve", "type": "bow" },
{ "name": "Short Bow", "type": "bow" },
{ "name": "Warden Bow", "type": "bow" },
{ "name": "Apprentice Warden Bow", "type": "bow" },
{ "name": "Uskang", "type": "bow" },
{ "name": "Ghoul King's Blade", "type": "sword" },
{ "name": "God Butcher", "type": "sword" },
{ "name": "Quinquennial Sword", "type": "sword" },
{ "name": "Second Quinquennial Sword", "type": "sword" },
{ "name": "Short Sword", "type": "sword" },
{ "name": "Apprentice's Wand", "type": "wand" },
{ "name": "Lesser Wand", "type": "wand" },
{ "name": "Oak Wand", "type": "wand" },
{ "name": "Starfire", "type": "wand" },
{ "name": "Unstable Scepter", "type": "wand" }
];
}
// 3.4
getWeapons() {
return this.weapons;
}
}
1. First we need to import Injectable from the @angular/core
library.
2. Then we declare this class to be an injectable using the @Injectable()
decorator we just imported. When implemented, @Injectable()
decorator will tell Angular that this class will be used for dependency injection.
3.1. We create our WeaponService
class which will wrap all of the code we want our WeaponsComponent
to access.
3.2. We create a weapons
variable, and use TypeScript's type-system to tell Angular that this object is going to be an array of any values.
3.3. We add our data to the weapons object by adding it into our constructor function, which is where we put the data we want a particular class to receive.
3.. Finally, we create a getWeapons
function to return our array of items. This is where we will often see http
calls being made however our data is internal, therefore we can leave out having to import the @angular/http
library.
Using our WeaponService as a dependency on our WeaponComponent
Now that our service is created, we need to do a few things in order to access it in our component. Import the service into our app/weapons.component.ts
file.
// app/weapons.component.ts
import { Component } from '@angular/core';
// import the WeaponService into our component
import { WeaponService } from 'app/weapon.service';
Register your service as a Provider
Next we need to pass WeaponService
into our components providers option. This is the section of our component where we add any services that it makes use of.
// app/friend.component.ts
@Component({
selector: 'my-weapons',
providers: [WeaponService],
styles: /** Styles are placed here **/
})
Inject it into the constructor() function
In order to our template access the WeaponService, we must inject it into the components constructor function. In this case, we are going to set our WeaponService
equal to _WeaponService
.
Once it's added to our constructor function, we are going to finish things off by assigning the weapons
variable the result of our getWeapons()
function.
// app/weapons.component.ts
export class WeaponsComponent {
componentName:string = 'WeaponsComponent';
// Inject WeaponService and assign it to _weaponService
constructor(private _weaponService: WeaponService) {
// Use .getWeapons() from app/weapon.service.ts to populate weapons object
this.weapons = _weaponService.getWeapons();
}
}
Final code for our app/weapons.component.ts
should look like:
// app/weapons.component.ts
import { Component } from '@angular/core';
import { WeaponService } from 'app/weapon.service';
@Component({
selector: 'my-weapons',
providers : [WeaponService],
styles: [
`div {
background-color:#EFEFEF;
margin-bottom:15px;
padding:15px;
border:1px solid #DDD;
box-shadow:2px 2px 2px 0 rgba(0, 0, 0, 0.3);
border-radius:3px;
}
h2 {
text-align: center;
}`
],
template: `
<h2>Hello from the {{componentName}}!</h2>
<div *ngFor="#w of weapons">
<h4> Name : {{w.name}} </h4> <h4>Type: {{w.type}}</h4>
</div>`
})
export class WeaponsComponent {
componentName:string = 'WeaponsComponent';
// Inject WeaponService and assign it to _weaponService
constructor(_weaponService: WeaponService) {
// Use .getWeapons() from app/weapon.service.ts to populate weapons object
this.weapons = _weaponService.getWeapons();
}
}
Adding WeaponsComponent to main AppComponent
Our second component is wired up, and we are ready to nest it within our top-level component. To accomplish this, we need to make a few modifications to our app.ts
file.
We have already seen how we can import core parts of Angular 2 via the ES2015 module syntax. Now, lets check out how we can use these same import
statements to access code inside of our project.
// app/app.ts
import { Component } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';
/** Nested Component */
import { WeaponsComponent } from 'app/weapons.component';
In order to access WeaponsComponent, we need to add it into the components directive array. We need to add <my-weapons></my-weapons>
to our inline-template or else it's not going to render. Also wrap this template in a <div>
tag in order to show Angular 2's style encapsulation.
// app/app.ts
import { Component } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';
/** Nested Component */
import { WeaponsComponent } from 'app/weapons.component';
@Component({
selector: 'my-app',
directives: [WeaponsComponent],
styles: [
`h1 {
color:#545454;
background:#02A8F4;
padding:15px;
box-shadow:2px 2px 2px 0 rgba(0, 0, 0, 0.3);
}`
]
template:
`<div>
<h1>Hello from the {{ componentName }}.</h1>
<my-weapons></my-weapons>
</div>`
})
export class AppComponent {
componentName:string = 'AppComponent';
}
bootstrap(AppComponent)