The objective of this step is to dynamically generate a list of three beers from a block of JSON data.
In this step we are creating a new component, BeerList
, that will render a list of beers.
Let's create the skeleton of the component using Angular CLI:
ng generate component BeerList
And a new component is generated :
$ ng generate component BeerList
create src/app/beer-list/beer-list.component.css (0 bytes)
create src/app/beer-list/beer-list.component.html (28 bytes)
create src/app/beer-list/beer-list.component.spec.ts (643 bytes)
create src/app/beer-list/beer-list.component.ts (280 bytes)
update src/app/app.module.ts (408 bytes)
The BeerList
component lives in the beer-list
folder inside angularbeers/src/app
. It is composed of four files:
beer-list.component.ts
: the definition and behavior of the componentbeer-list.component.html
: the component templatebeer-list.component.css
: the component stylebeer-list.component.spec.ts
: the component test specifications
In beer-list.component.ts
we are going to do some changes to adapt the component to our needs.
-
Modify the selector to make it
beer-list
. The selector css selector that identifies this component in a template. -
Add the data model (a simple array of beers in object literal notation)
angularbeers/src/app/beer-list/beer-list.component.ts
:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'beer-list',
templateUrl: './beer-list.component.html',
styleUrls: ['./beer-list.component.css']
})
export class BeerListComponent implements OnInit {
beers = [
{
"alcohol": 8.5,
"name": "Affligem Tripel",
"description": "The king of the abbey beers. It is amber-gold and pours with a deep head and original aroma, delivering a complex, full bodied flavour. Pure enjoyment! Secondary fermentation in the bottle."
},
{
"alcohol": 9.2,
"name": "Rochefort 8",
"description": "A dry but rich flavoured beer with complex fruity and spicy flavours."
},
{
"alcohol": 7,
"name": "Chimay Rouge",
"description": "This Trappist beer possesses a beautiful coppery colour that makes it particularly attractive. Topped with a creamy head, it gives off a slight fruity apricot smell from the fermentation. The aroma felt in the mouth is a balance confirming the fruit nuances revealed to the sense of smell. This traditional Belgian beer is best savoured at cellar temperature "
}
];
constructor() { }
ngOnInit() {
}
}
In Angular, the view is a projection of the model through the HTML template. This means that whenever the model changes, Angular refreshes the appropriate binding points, which updates the view.
The view component is constructed by Angular from this template:
angularbeers/src/app/beer-list/beer-list.component.html
:
<ul>
<li *ngFor="let beer of beers">
<span>{{beer.name}}</span>
<p>{{beer.description}}</p>
</li>
</ul>
In the template we replaced the hard-coded beer list with the NgFor directive :
-
The
*ngFor="let beer of beers"
attribute in the<li>
tag is an Angular repeater directive. The repeater tells Angular to create a<li>
element for each beer in the list using the<li>
tag as the template. -
The expressions wrapped in curly braces (
{{beer.name}}
and{{beer.description}}
) will be evaluated and replaced by the value of the expressions.
The Angular expressions in curly braces ({{beer.name}}
and {{beer.description}}
) denote bindings, which are referring to our application model, which is set up in our beerList.component
component.
Our application is bootstrapped as a browser application (a webapp) on angularbeers/src/app/main.ts
:
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
And it's loaded using a main module, angularbeers/src/app/app.module.ts
:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { BeerListComponent } from './beer-list/beer-list.component';
@NgModule({
declarations: [
AppComponent,
BeerListComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Here we see that currently our app has two components, the main one (AppComponent
) and our recently created BeerList
.
Now we can insert BeerList
inside the template of AppComponent
instead of the hardcoded list of beers:
angularbeers/src/app/app.component.html
:
<div>
<h1>
Welcome to {{title}}!
</h1>
<beer-list></beer-list>
</div>
Although the component is not yet doing very much, it plays a crucial role. By providing context for our data model, the component allows us to establish data-binding between the model and the view. We connected the dots between the presentation, data, and logic.
Add another binding to beer-list.component.html
. For example:
<p>Total number of beers: {{beers.length}}</p>
Create a new model property in the component and bind to it from the template. For example:
name = "World";
Then add a new binding to beer-list.component.html
:
<p>Hello, {{name}}!</p>
Refresh your browser and verify that it says "Hello, World!".
Create a repeater in beer-list.component.html
that constructs a simple table:
<table>
<tr><th>row number</th></tr>
<tr *ngFor="let i of [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
</table>
Now, make the list 1-based by incrementing i by one in the binding:
<table>
<tr><th>row number</th></tr>
<tr *ngFor="let i of [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
Extra points: try and make an 8x8 table using an additional *ngFor
.
You now have a dynamic app that features separate model, view, and controller components. Now, let's go to step 03 to learn how to add full text search to the app.