30.6. Events Can Call Functions¶
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.
30.6.1. Modify the HTML¶
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 2 3 4 5 6 7 8 9 10
<div class='movies col-4'> <h3>Movies to Watch</h3> <ol> <li *ngFor ="let movie of movies">{{movie}}</li> </ol> <hr> <input #newMovie (keyup.enter)='addMovie(newMovie.value)' type='text' placeholder="Enter Movie Title Here"/> <button (click)='addMovie(newMovie.value)'>Add</button> <p>{{newMovie.value}}</p> </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.
30.6.2. Define the Function¶
Open movie-list.component.ts
and examine the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import { Component, OnInit } from '@angular/core';
@Component({
selector: 'movie-list',
templateUrl: './movie-list.component.html',
styleUrls: ['./movie-list.component.css']
})
export class MovieListComponent implements OnInit {
movies = ['Toy Story', 'The Shining', 'Sleepless in Seattle', 'The Martian'];
constructor() { }
ngOnInit() {
}
}
|
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:1 2 3 4 5 6 7 8 9 10 11 12
export class MovieListComponent implements OnInit { movies = ['Toy Story', 'The Shining', 'Sleepless in Seattle', 'The Martian']; constructor() { } ngOnInit() { } addMovie (newTitle: string) { } }
Notice that we have to declare the data type for the
newTitle
parameter.Now add code to
push
the new title to themovies
array:1 2 3 4 5 6 7 8 9 10 11 12
export class MovieListComponent implements OnInit { movies = ['Toy Story', 'The Shining', 'Sleepless in Seattle', 'The Martian']; constructor() { } ngOnInit() { } addMovie (newTitle: string) { this.movies.push(newTitle); } }
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:
30.6.3. Tidying Up the Display¶
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.
30.6.3.1. Clear the Input Box¶
After the user submits a new title, we can clear the input box by setting its value to be the empty string (
''
). Openmovie-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 callsaddMovie
. 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.
30.6.3.2. Check for Duplicates¶
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:
1 2 3 4 5 | addMovie (newTitle: string) {
if(!this.movies.includes(newTitle)){
this.movies.push(newTitle);
}
}
|
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.
30.6.4. Bonus¶
To boost your skills, try these optional tasks to enhance your work:
Modify
addMovie
to reject the empty string as a title.Use
*ngIf
to display an error message if the user does not enter a title or submits a title that is already on the list.Add CSS to change the color of the error message.
The example-solutions
branch of the Angular repo shows completed code for
the bonus tasks.
30.6.5. Check Your Understanding¶
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>