Learning Objectives: Angular
Components
Learners will be able to…
Write computed property getters based on properties in
Angular components
Write Angular template code, including mustache syntax
Bind directives to Angular component state, dynamically
changing the UI
Write event listeners to communicate between Angular
components and templates
info
Make Sure You Know
Basic HTML, CSS, and JavaScript.
Creating Our Todo App
We’ll start by going into proj —> todomvc —> src —> app —>
[Link] - our “app class” - and define a todo item.
Let’s add this line to the body of our class:
allItems = [
{
id: [Link](),
title: 'Todo Item',
completed: false,
editing: false
}
];
get items() {
return [Link];
}
so that our file looks like this:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
title = 'todomvc';
allItems = [
{
id: [Link](),
title: 'Todo Item',
completed: false
}
];
get items() {
return [Link];
}
}
There are many things going on in this file!
We start off by importing the Component class from the @angular/core
library. We use this Component definition as a decorator, providing
information about what this file contains to the project.
selector defines the way we’ll call this component from other files in the
project. When we call our app component, we’ll use the tag <app-root>.
templateUrl and styleUrls are pretty self-explanatory, aren’t they? With
templateUrl, we’re specifying the location of the template file, and with
styleUrls, we’re specifying where to find the CSS stylesheet files that
decorate this component.
Next, let’s find and open our [Link] file located in the same
directory. So far, We’ve added a single todo item to our class. How do we
render it using Angular template syntax? First, take a look at this particular
code:
<ul class="todo-list" *ngFor="let item of items">
<li>{{[Link]}}</li>
</ul>
Before we explain it, can you identify how we’re iterating over our list of
todo items in the template?
*ngFor is an Angular directive! Just like the v-for directive in VueJS, *ngFor
tells the template to access the list of todo items from the class and tells it to
iterate over them.
Revise the Template code so that this section looks like this now:
<ul class="todo-list" *ngFor="let item of items">
<li class="todo-item">
<!-- NOTE - change to view-todo -->
<div class="view">
<!-- NOTE - change to toggle-done -->
<input class="toggle" type="checkbox">
<label>{{[Link]}}</label>
<!-- NOTE: Change to remove-todo -->
<button class="destroy"></button>
</div>
<!-- NOTE: Change to edit-todo -->
<!-- <input class="edit" type="text"
placeholder="Exiting todo to edit"> -->
</li>
</ul>
Angular uses mustache syntax ({{ and }}) to render variable values from
the component to the template.
create
TESTING YOUR APPLICATION
As you progress through the course, feel free to test your developing
application by either running it yourself manually inside the Terminal:
ng serve --host [Link] --port 4200 --disable-host-check
Or by clicking the NG SERVE button:
Make sure you are in the correct directory (proj/todomvc) before
running your app!
Remember that you may need to reload the web page before it starts
showing the new changes.
Lastly, to exit the app, use the keyboard shortcuts: control + c or ctrl +
c.
Computed Properties
definition
KEY
“app class” = [Link]
“todo app” = [Link]
Previously, in [Link], we created a computed property called
items using the get keyword. This is so that later in the lesson, we can add
filtering to our todo app.
We’ll add another computed property - remaining to change the content in
our footer from:
<span class="todo-count">
<strong>1</strong>
<span>item left</span>
</span>
to
<span class="todo-count">
<strong>{{remaining}}</strong>
<span>{{remaining == 1 ? 'item' : 'items'}} left</span>
</span>
Go ahead and make the above change inside [Link].
If the code above looks similar, it is because it is exactly the same as our
VueJS template code from the VueJS course!
challenge
Try this:
Write the app class code for the above computed property yourself
inside the [Link] file.
Sample Solution
It should look like:
get remaining() {
return [Link](todo =>
![Link]).length;
}
We’re filtering our todo items based on their completion state. That
is, if a todo item is incomplete, it is remaining. And then we get how
many todo items that is using the length property.
Conditional Rendering in Angular
If we use the *ngFor directive to iterate over items using a for loop, how do
you think we do conditional rendering in Angular? That is, if a condition is
true, then we’ll want to show it.
The *ngIf directive is how we conditionally render content in Angular.
In our todo app ([Link]), we want to make it so the elements
under <section class="main"> (our todo list) and <footer> only display if
there are todo items in our todo list items.
challenge
Try this:
Make it so the elements under <section class="main"> (our todo
list - [Link]) and <footer> only display if there are
todo items in our todo list items.
Sample Solution
The directive we’re using is *ngIf and we want to set it equal (=) to a
condition that makes it so our elements only display if the number of
all todo items (with filter values all, active, or completed) is greater
than 0. That looks like this:
*ngIf="[Link] > 0"
1. Change <section class="main"> to <section class="main"
*ngIf="[Link] > 0">
2. Change <footer class="footer"> to <footer class="footer"
*ngIf="[Link] > 0">.
Getting Our Filters Working
We created the computed property items in our app class. Now we will
make use of this computability.
Add this to [Link] within export class AppComponent{}.
filter: 'all' | 'active' | 'done' = 'all';
The default value for our filters is all, but we are using a union (the pipe
| operators) to define options. If we make a typo in defining a name for our
filters in our template, TypeScript will warn us when our code compiles in
the browser.
Additionally, let’s define the items computed property.
get items() {
if ([Link] === 'all') {
return [Link];
}
return [Link]((item) => [Link] ===
'completed' ? [Link] : ![Link]);
}
Lastly, we will modify our template code in our todo app
([Link]) to set a filter value when we click a filtering option on
our todo app. We’ll use the click event binding. Here’s an example:
<a href="#/all" (click)="filter = 'all'">All</a>
(source: [Link]
challenge
Try this:
Modify the active and completed filters to match the all filter
above.
Sample Solution
Before:
<ul class="filters">
<li>
<a href="#/all">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
After:
<ul class="filters">
<li>
<a href="#/all" (click)="filter = 'all'">All</a>
</li>
<li>
<a href="#/active" (click)="filter =
'active'">Active</a>
</li>
<li>
<a href="#/completed" (click)="filter =
'completed'">Completed</a>
</li>
</ul>
Current [Link] file
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
title = 'todomvc';
allItems = [
{
id: [Link](),
title: 'Todo Item',
completed: false
}
];
get items() {
if ([Link] === 'all') {
return [Link];
}
return [Link]((item) => [Link] ===
'completed' ? [Link] : ![Link]);
}
get remaining() {
return [Link](todo => ![Link]).length;
}
filter: 'all' | 'active' | 'completed' = 'all';
}
Current [Link] file
<section class="todoapp">
<header class="header">
<h1>Todos</h1>
<input autofocus class="new-todo" type="text"
placeholder="What needs to be done?">
</header>
<section class="main" *ngIf="[Link] > 0">
<input class="toggle-all" id="toggle-all"
type="checkbox" checked="false">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list" *ngFor="let item of items">
<li class="todo-item">
<!-- NOTE - change to view-todo -->
<div class="view">
<!-- NOTE - change to toggle-done -->
<input class="toggle" type="checkbox">
<label>{{[Link]}}</label>
<!-- NOTE: Change to remove-todo -->
<button class="destroy"></button>
</div>
<!-- NOTE: Change to edit-todo -->
<!-- <input class="edit" type="text"
placeholder="Exiting todo to edit"> -->
</li>
</ul>
</section>
<footer class="footer" *ngIf="[Link] > 0">
<span class="todo-count">
<strong>{{remaining}}</strong>
<span>{{remaining == 1 ? 'item' : 'items'}}
left</span>
</span>
<ul class="filters">
<li>
<a href="#/all" (click)="filter = 'all'">All</a>
</li>
<li>
<a href="#/active" (click)="filter =
'active'">Active</a>
</li>
<li>
<a href="#/completed" (click)="filter =
'completed'">Completed</a>
</li>
</ul>
<button class="clear-completed">
Clear completed todos
</button>
</footer>
</section>
create
TESTING YOUR APPLICATION
As you progress through the course, feel free to test your developing
application by either running it yourself manually inside the Terminal:
ng serve --host [Link] --port 4200 --disable-host-check
Or by clicking the NG SERVE button:
Make sure you are in the correct directory (proj/todomvc) before
running your app!
Remember that you may need to reload the web page before it starts
showing the new changes.
Lastly, to exit the app, use the keyboard shortcuts: control + c or ctrl +
c.
Completing Todo Items
definition
KEY
“app class” = [Link]
“todo app” = [Link]
We’ll want to connect the input[type="checkbox"] to its complete state in
the component. We’ll also want to apply styling to the <li class="todo-
item"> element group and the checkbox.
Add the following method to the app class ([Link] file):
toggleCompletion(todo) {
[Link] = ![Link];
}
important
NOTE
TypeScript does not permit a function signature without a type
specified, as you will find out when you build this project. To bypass
this, find and open the [Link] file by going to proj —> todomvc
—> [Link]
Then add "noImplicitAny": false, right above the line that reads
"noImplicitReturns": true, so that it looks like this:
/* To learn more about this file see:
[Link] */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2017",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
To learn more about [Link], see
[Link]
Then go back into our todo app ([Link]) and change:
<input class="toggle" type="checkbox">
to:
<input class="toggle" type="checkbox"
(click)="toggleCompletion(item)"
[checked]="[Link]">
This binds the state of our todo item to its checked state in the template. Try
running the app now. Click the checkbox to the left of our todo item and
what do you notice?
.guides/img/todo-check
If we click a todo item and complete it, our strikethrough class doesn’t
apply. We want to apply a conditional class binding to our todo items.
(source: [Link]
We’ll set a [[Link]] attribute on our <li class="todo-item">
elements to be set if our todo is completed (or [Link]).
challenge
Try this:
Set a [[Link]] attribute on our <li class="todo-item">
elements to be set if our todo is completed (or [Link]).
Sample Solution
Change
<li class="todo-item">
to
<li class="todo-item" [[Link]]="[Link]">
Checking and Unchecking All Our Todos
Add this method to [Link]:
toggleAll() {
[Link](todo => [Link] =
![Link]);
}
and in our todo app template ([Link]), do the following to the
<input class="toggle-all"> element:
1. Add a conditional so that this element only displays if allItems actually
has todo items in it.
2. Bind an attribute checked so that this element is checked if there are no
remaining todo items.
3. Make it so toggleAll runs if this element is (click)ed.
(source: [Link]
Sample Solution
Change:
<input class="toggle-all" id="toggle-all" type="checkbox"
checked="false">
to:
<input class="toggle-all" id="toggle-all" type="checkbox"
*ngIf="[Link]" [checked]="remaining === 0"
(click)="toggleAll()">
Removing Completed Todo Items
Let’s add the completed computed property to our app class:
get completed() {
return [Link](todo => [Link]);
}
And add a the following method:
removeCompleted() {
[Link] = [Link](todo =>
![Link]);
}
challenge
Try this:
Add a conditional render that only shows the Clear completed
button if completed has todo items in it
Bind a (click) handler to the <button class="clear-completed">
element that runs the removeCompleted() method when clicked
Sample Solution
The tasks above can be accomplished by changing:
<button class="clear-completed">
Clear completed todos
</button>
to:
<button class="clear-completed" *ngIf="[Link] > 0"
(click)="removeCompleted()">Clear completed
todos</button>
Next Up
In the next assignments, we’ll learn how to create, edit, and delete todo
items in Angular, save our todo list in the browser’s localStorage, and
communicate and send data between Angular components.
Current [Link] file
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
title = 'todomvc';
allItems = [
{
id: [Link](),
title: 'Todo Item',
completed: false
}
];
get items() {
if ([Link] === 'all') {
return [Link];
}
return [Link]((item) => [Link] ===
'completed' ? [Link] : ![Link]);
}
get remaining() {
return [Link](todo => ![Link]).length;
}
filter: 'all' | 'active' | 'completed' = 'all';
toggleCompletion(todo) {
[Link] = ![Link];
}
toggleAll() {
[Link](todo => [Link] =
![Link]);
}
get completed() {
return [Link](todo => [Link]);
}
removeCompleted() {
[Link] = [Link](todo =>
![Link]);
}
}
Current [Link] file
<section class="todoapp">
<header class="header">
<h1>Todos</h1>
<input autofocus class="new-todo" type="text"
placeholder="What needs to be done?">
</header>
<section class="main" *ngIf="[Link] > 0">
<input class="toggle-all" id="toggle-all"
type="checkbox" *ngIf="[Link]"
[checked]="remaining === 0" (click)="toggleAll()">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list" *ngFor="let item of items">
<li class="todo-item"
[[Link]]="[Link]">
<!-- NOTE - change to view-todo -->
<div class="view">
<!-- NOTE - change to toggle-done -->
<input class="toggle" type="checkbox"
(click)="toggleCompletion(item)"
[checked]="[Link]">
<label>{{[Link]}}</label>
<!-- NOTE: Change to remove-todo -->
<button class="destroy"></button>
</div>
<!-- NOTE: Change to edit-todo -->
<!-- <input class="edit" type="text"
placeholder="Exiting todo to edit"> -->
</li>
</ul>
</section>
<footer class="footer" *ngIf="[Link] > 0">
<span class="todo-count">
<strong>{{remaining}}</strong>
<span>{{remaining == 1 ? 'item' : 'items'}}
left</span>
</span>
<ul class="filters">
<li>
<a href="#/all" (click)="filter = 'all'">All</a>
</li>
<li>
<a href="#/active" (click)="filter =
'active'">Active</a>
</li>
<li>
<a href="#/completed" (click)="filter =
'completed'">Completed</a>
</li>
</ul>
<button class="clear-completed" *ngIf="[Link]
<button class="clear-completed" *ngIf="[Link]
> 0" (click)="removeCompleted()">Clear completed
todos</button>
</footer>
</section>
Current [Link] file
/* To learn more about this file see:
[Link] */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2017",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
create
TESTING YOUR APPLICATION
As you progress through the course, feel free to test your developing
application by either running it yourself manually inside the Terminal:
ng serve --host [Link] --port 4200 --disable-host-check
Or by clicking the NG SERVE button:
Make sure you are in the correct directory (proj/todomvc) before
running your app!
Remember that you may need to reload the web page before it starts
showing the new changes.
Lastly, to exit the app, use the keyboard shortcuts: control + c or ctrl +
c.