For the user input practice, we set the keyup and click events equal to
true. This just checks to see if these events occur. When they do, the
input is stored in newMovie, and the page refreshes.
To perform more complicated tasks in response to the user's actions, we can call a function when an event occurs. The syntax for this is:
(event) = "functionName(arguments...)"
Changing the movie list displayed on the web page requires us to modify the
movies array in the movie-list.component.ts file. We will do this by
creating an addMovie function and linking it to our event handlers.
Let's change our code in movie-list.component.html to call the function
addMovie and pass the new movie title as the argument.
On lines 7 and 8, replace true with the function call:
1<div class='movies col-4'>
2 <h3>Movies to Watch</h3>
3 <ol>
4 <li *ngFor ="let movie of movies">{{movie}}</li>
5 </ol>
6 <hr>
7 <input #newMovie (keyup.enter)='addMovie(newMovie.value)' type='text' placeholder="Enter Movie Title Here"/>
8 <button (click)='addMovie(newMovie.value)'>Add</button>
9 <p>{{newMovie.value}}</p>
10</div>
Now when the user taps "Enter" or clicks the "Add" button after typing, the
input newMovie.value gets sent to the function.
Since our plan is to use a function to add the new movie to the array, we no
longer need the title to appear below the input box. Remove
<p>{{newMovie.value}}</p> from line 9.
Open movie-list.component.ts and examine the code:
1import { Component, OnInit } from '@angular/core';
2
3@Component({
4 selector: 'movie-list',
5 templateUrl: './movie-list.component.html',
6 styleUrls: ['./movie-list.component.css']
7})
8export class MovieListComponent implements OnInit {
9 movies = ['Toy Story', 'The Shining', 'Sleepless in Seattle', 'The Martian'];
10
11 constructor() { }
12
13 ngOnInit() {
14 }
15}
The movies array stores the titles displayed on the web page, and we want to
update this when the user supplies new information.
Declare a function called addMovie that takes one parameter:
1export class MovieListComponent implements OnInit {
2 movies = ['Toy Story', 'The Shining', 'Sleepless in Seattle', 'The Martian'];
3
4 constructor() { }
5
6 ngOnInit() {
7 }
8
9 addMovie (newTitle: string) {
10
11 }
12}
Notice that we have to declare the data type for the newTitle parameter.
Now add code to push the new title to the movies array:
1export class MovieListComponent implements OnInit {
2 movies = ['Toy Story', 'The Shining', 'Sleepless in Seattle', 'The Martian'];
3
4 constructor() { }
5
6 ngOnInit() {
7 }
8
9 addMovie (newTitle: string) {
10 this.movies.push(newTitle);
11 }
12}
The keyword this is required.
Note
It is a common practice to put constructor and functions like
ngOnInit AFTER the variable declarations but BEFORE any custom
functions.
Save the changes and then refresh the page. Enter a new title to verify that it appears in the movie list. Your page should look something like:
Notice that after adding a new movie to the list, the text remains in the input box. If we click "Add" multiple times in a row, we would see something like:
Let's modify the code to try to prevent this from happening.
After the user submits a new title, we can clear the input box by setting its
value to be the empty string (''). Open movie-list.component.html
and modify the input statement as follows:
<input #newMovie (keyup.enter)="addMovie(newMovie.value); newMovie.value = ''" type="text" placeholder="Enter Movie Title Here"/>
When keyup.enter occurs, the code calls addMovie. Once control
returns from the function, newMovie.value is set equal to '', which
clears any text from the input box.
Since the user can also click the "Add" button to submit a title, we need to
modify the <button> element as well:
<button (click)="addMovie(newMovie.value); newMovie.value = ''">Add</button>
Now newMovie.value is set equal to '', when "Enter" or "Add" are used
to submit data.
Try It
Refresh the page and verify that the input box gets cleared after each new title.
Even though we clear the input box, there is nothing to prevent the user from entering the same movie multiple times. While some fans may want to watch a film twenty times in a row, let's have our code prevent repeats.
Recall that the includes method checks if an array contains a particular element. The method gives us several ways to check for a repeated title. One possibility is:
1addMovie (newTitle: string) {
2 if(!this.movies.includes(newTitle)){
3 this.movies.push(newTitle);
4 }
5}
If the movies array already contains newTitle, then the includes
method returns true. The NOT operator (!) flips the result to
false, and line 3 is skipped.
Try It
Refresh the page and verify that you cannot enter a duplicate title.
To boost your skills, try these optional tasks to enhance your work:
addMovie to reject the empty string as a title.*ngIf to display an error message if the user does not enter a title
or submits a title that is already on the list.The example-solutions branch of the Angular repo shows completed code for
the bonus tasks.
Assume that we have an Angular project that presents users with a list of potential pets:
Question
Which of the following calls the addPet function when the user clicks
on one of the potential pets:
<li>{{pet}}</li><li (click) = "true">{{pet}}</li><li #addPet (click) = "true">{{pet}}</li><li (click) = "addPet(pet)">{{pet}}</li>Question
When the user moves the mouse over an animal, we want to store its name in
the newFriend variable. Which of the following accomplishes this?
<li (mouseover) = "pet.name">{{pet}}</li><li #newFriend (mouseover) = "pet.name">{{pet}}</li><li (mouseover) = "newFriend = pet.name">{{pet}}</li><li (mouseover) = "newFriend">{{pet}}</li>