Getting Started With Angular 2 Step by Step: 3 - Your Second Component And Angular 2 Data Binding
UPDATE (11th October 2017): This Angular 2 tutorial has been updated to the latest version of Angular (Angular 4). Note that the Angular team has re-branded the terms Angular 2
to Angular
and Angular 1.x
to AngularJS
which are now the official names of these frameworks.
This is the third article on the Getting Started with Angular 2 Step by Step series if you missed the previous articles go and check them out!
In this article you are going to create your second component and you are going to get a better understanding of the different possibilities Angular 2 offers in terms of data bindings. Let’s get to it!
The Code Samples
You can download the code samples for this article in this GitHub repository. You can also check the unbelievably cool online editor Stackblitz which has awesome support for writing Angular prototypes on the web. My recommendation is that you follow the tutorial along using your own development environment, that way you get to enjoy the full Angular developer experience. But if you don’t have time or energy to set it up right now, then try Stackblitz.
A Person Details Component
We are interested in learning more information about these Star Wars characters. That’s why we are going to update our application so that when you click on a person within the PeopleListComponent
we show a more detailed information for the person we have selected.
We are going to start by showing that detail information directly within the PeopleListComponent
and then we are going to extract it into its very own PersonDetailsComponent
.
The final solution should look something like this:
Our People List Component Right Now
This is how the PeopleListComponent
looked like at the end of the last exercise:
import { Component, OnInit } from '@angular/core'
import { Person } from '../person'
import { PeopleService } from '../people.service'
@Component({
selector: 'app-people-list',
template: `
<!-- this is the new syntax for ng-repeat -->
<ul>
<li *ngFor="let person of people">
{{person.name}}
</li>
</ul>
`,
styleUrls: ['./people-list.component.scss'],
})
export class PeopleListComponent implements OnInit {
people: Person[]
constructor(private peopleService: PeopleService) {}
ngOnInit() {
this.people = this.peopleService.getAll()
}
}
We are retrieving a list of Star Wars characters for our PeopleService
and showing them to our users. We achieve that by taking advantage of the *ngFor
directive and the interpolation binding.
Showing Person Details When Clicking on a Character
Now we want to be able to select a person whenever we click on it. How do we do that? We use the Angular 2 (click)
event binding.
@Component({
selector: 'app-people-list',
template: `
<!-- this is the new syntax for ng-repeat -->
<ul>
<li *ngFor="let person of people">
<!-- HERE: add a element with click event binding -->
<a href="#" (click)="selectPerson(person)">
{{person.name}}
</a>
</li>
</ul>
`
})
The (click)="selectPerson(person)"
event binding tells Angular 2 to call the selectPerson
component method whenever a user clicks on the a
element.
We can now update our PeopleListComponent
to provide that method to the view:
export class PeopleListComponent implements OnInit {
people: Person[] = []
selectedPerson: Person
constructor(private _peopleService: PeopleService) {}
ngOnInit() {
this.people = this._peopleService.getAll()
}
// HERE is the new method
selectPerson(person) {
this.selectedPerson = person
}
}
So now, whenever a user clicks on a person in the list, Angular 2 will call the selectPerson
method that will in turn update the selectedPerson
property within the PeopleListComponent
.
If we now use that selectedPerson
property in the template we get to the solution we were looking for:
@Component({
selector: 'app-people-list',
template: `
<!-- this is the new syntax for ng-repeat -->
<ul>
<li *ngFor="let person of people">
<a href="#" (click)="selectPerson(person)">
{{person.name}}
</a>
</li>
</ul>
<!-- HERE: we add the template for the person details -->
<section *ngIf="selectedPerson">
<h2>You selected: {{selectedPerson.name}}</h2>
<h3>Description</h3>
<p>
{{selectedPerson.name}} weights {{selectedPerson.weight}} and is {{selectedPerson.height}} tall.
</p>
</section>
`
})
Notice how we used the *ngIf
binding (like ng-if
in AngularJS) another structural directive like *ngFor
that manipulates the DOM based on an expression. In this case, it shows the person detail information only when a person has been selected.
The completed PeopleListComponent
with the person details looks like this:
import { Component, OnInit } from '@angular/core'
import { Person } from '../person'
import { PeopleService } from '../people.service'
@Component({
selector: 'app-people-list',
template: `
<!-- this is the new syntax for ng-repeat -->
<ul>
<li *ngFor="let person of people">
<a href="#" (click)="selectPerson(person)">
{{person.name}}
</a>
</li>
</ul>
<section *ngIf="selectedPerson">
<h2>You selected: {{selectedPerson.name}}</h2>
<h3>Description</h3>
<p>
{{selectedPerson.name}} weights {{selectedPerson.weight}} and is {{selectedPerson.height}} tall.
</p>
</section>
`,
})
export class PeopleListComponent implements OnInit {
people: Person[] = []
selectedPerson: Person
constructor(private peopleService: PeopleService) {}
ngOnInit() {
this.people = this.peopleService.getAll()
}
selectPerson(person: Person) {
this.selectedPerson = person
}
}
What Are Angular 2 Event Bindings?
Angular 2 data binding works a little bit different than it did in AngularJS.
Angular 2 offers a wide variety of binding options. You’ve seen the interpolation binding that provides a one way data binding from the component to the template to display data. And now you’ve learned about the event binding that provides a one way data binding between the template and the underlying component.
The event binding syntax lets you bind component methods directly to DOM events. You no longer need to have custom Angular bindings like we had in AngularJS with ng-click
, ng-change
, etc… you just bind to (click)
, (change)
or whichever event that you desire.
Extracting Person Details Into Its Own Component
If you look at the previous code sample for PeopleComponentList
it feels like there’s too much going on in there. When you start having a nagging feeling of theres-too-much-going-on-here is the perfect signal that you need to start thinking about breaking things down.
A person’s details are a perfect candidate for having its own component. Let’s create a PersonDetailsComponent
! Yey!
We start creating a new person-details.component.ts
file and module to contain our new component. Again the Angular CLI comes to the rescue, type the following to generate the new component:
PS> ng g c -it -is person-details
# ng generate component --inline-template --inline-style
This will include the piece of the template related to displaying person details information:
import { Component, OnInit } from '@angular/core'
import { Person } from '../person'
@Component({
selector: 'app-person-details',
template: `
<!-- we moved the details template here -->
<section *ngIf="person">
<h2>You selected: {{person.name}}</h2>
<h3>Description</h3>
<p>
{{person.name}} weights {{person.weight}} and is {{person.height}} tall.
</p>
</section>
`,
styles: [],
})
export class PersonDetailsComponent implements OnInit {
person: Person
constructor() {}
ngOnInit() {}
}
There it is. So now we have a component that represents person details and that we can reuse and compose with other components in our application. Yippi! But how can we send in data so that person
property gets some person to display? Or in other words, how can we bind a person to this component?
We use the Angular 2 Input
decorator:
// include this on top of person-details.component.ts
import { Component, Input } from '@angular/core'
export class PersonDetailsComponent {
// update PersonDetailsComponent
// to decorate the person property with @Input()
@Input() person: Person
}
This will mark the person
property as an input to the PersonDetailsComponent
and will allow other components to send a person into the component. How? By using Angular 2 property bindings like this:
<app-person-details [person]="selectedPerson"></app-person-details>
Indeed, we can update the PeopleListComponent
to make use of the new component. Remember, we normally need to start by adding the new component to app.module.ts
so our application will be aware of it. Since we are used the Angular CLI, it should’ve taken care of this plumbing for us. If you check app.module.ts
you should be able to see that the following bits of code have been already added. First the import declaration:
import { PersonDetailsComponent } from './person-details/person-details.component'
Then the component is included within the declarations
property of the NgModule
decorator:
@NgModule({
declarations: [
AppComponent,
PeopleListComponent,
PersonDetailsComponent, // Here is our new component!
],
imports: [BrowserModule, FormsModule, HttpModule],
providers: [PeopleService],
bootstrap: [AppComponent],
})
export class AppModule {}
The next step is to use our brand new component within the template of PeopleListComponent
:
<!-- this is the new syntax for ng-repeat -->
<ul>
<li *ngFor="let person of people">
<a href="#" (click)="selectPerson(person)">
{{person.name}}
</a>
</li>
</ul>
<app-person-details [person]="selectedPerson"></app-person-details>
The complete PeopleListComponent
looks like this:
import { Component, OnInit } from '@angular/core'
import { Person } from '../person'
import { PeopleService } from '../people.service'
@Component({
selector: 'app-people-list',
template: `
<!-- this is the new syntax for ng-repeat -->
<ul>
<li *ngFor="let person of people">
<a href="#" (click)="selectPerson(person)">
{{person.name}}
</a>
</li>
</ul>
<app-person-details [person]="selectedPerson"></app-person-details>
`,
styleUrls: ['./people-list.component.scss'],
})
export class PeopleListComponent implements OnInit {
selectedPerson: Person
people: Person[]
constructor(private peopleService: PeopleService) {}
ngOnInit() {
this.people = this.peopleService.getAll()
}
selectPerson(person: Person) {
this.selectedPerson = person
}
}
And you are done! If you check your browser right now (remember ng serve --open
or ng s -o
to start the web server and load your app in the browser) you should be able to test that whenever you click on a Star Wars person its details are shown. Good job!
What are Angular 2 Property Bindings?
Angular 2 property bindings are like the reverse to event bindings as they provide a one-way data binding between your component and your template.
They allow you to bind to custom properties when communicating between components just like you saw in this exercise but they also let you bind to native DOM properties like the src
of an image element. Property bindings are yet another example of a way that Angular 2 does away with a lot of custom directives from AngularJS: there’s no need for the ng-src
directive when you can just bind directly to [src]
.
Want to Learn more about Angular 2 Data Bindings?
Later in this series I will write an article looking more closely at the Angular 2 data binding system, but if you are curious right now, here are a couple of articles that will tell you more about how Angular 2 Data Binding works:
- Angular 2 Basics: Template Syntax describes the basics of Angular 2 template syntax including interpolation, event and property bindings.
- The Angular 2 Cheatsheet has a nice compilation of different useful bindings that you can use.
- Change Detection in Angular 2 a great article by Angular core member Victor Savkin
- Angular 2 Change Detection Explained another great blog post at the thoughtram blog
Concluding
Great job! You have already created two Angular 2 components, learned a lot about Angular 2 template syntax with interpolation, event bindings, property bindings, *ngFor
, *ngIf
, you have created your first service and injected using Angular 2 DI. Think about all the stuff that you’re going to build with Angular 2!! :)
Up next! Routing! I think… (checking GitHub)… Routing!!
Have a great day!
Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.