Saving and Reading Data with Cloud Firestore – ngIf and ngFor | My First App with Angular 8

Let’s make a series of entries in which we’ll create an application with Angular 8 and Firebase. The application will be a ToDo App, that is, a task manager app. It will use the Cloud Firestore database, and Firebase Authentication for the login system.

Related entries

  1. Introduction 
  2. Configuring Firebase and Bootstrap in Angular. Understanding Cloud Firestore
  3. Creating our first component – Click event  
  4. Our first reactive form
  5. Saving and reading data in Cloud Firestore – ngIf and ngFor <– You are here
  6. Introduction to Routing
  7. Login system with Angular and Firebase
  8. Releasing our Angular App in Azure with VS Code

As I publish the articles, I will be placing the links here above☝.

Saving and reading data in Cloud Firestore

We will work with the functionality of saving the information that the user enters in our form. For that we are going to implement the function that the Save button tries to execute. We’re going to paste this implementation into the todo-form.component.ts class:

The first thing we do in this method is validate that the form is not invalid, if it is invalid then we stop the execution of the function, otherwise, we continue. Below we will place the code to send the information to Firebase. Let’s create a model which will represent a task, and we will also create a service which will encapsulate the methods to work with Cloud Firestore with respect to the creation, editing, deletion and reading of tasks from the database. Let’s start by creating our model, let’s execute the following command in the terminal:

ng generate interface todo/models/todo

This command is going to generate an interface. Let’s place the following properties in it:

This interface represents the fields that we are going to save in a Cloud Firestore document. We see that we have strings, a boolean and two dates. We will also create another model that will serve to represent the data of a task, and that will also contain the task Id created by Cloud Firestore. For that, we execute the following command in the terminal:

ng generate interface todo/models/todoViewModel

Then edit the interface as follows:

Now, we are going to create the service that is going to encapsulate the operations with Cloud Firestore, for that, let’s go back to the terminal and execute the following command:

ng generate service todo/services/todo

This command is going to generate a service. Let’s write the following code in it:

As we can see, here we have methods to read the tasks of our todos collection in Cloud Firestore, we can create tasks, edit tasks, make partial editions, and we can delete tasks from our collection. Notice that we put the name of the collection as a field of the class, called todoCollectionName. For now, what we are going to do is consume the SaveTodo method, which receives a Todo as parameter, and saves it in a document within the todos collection.

Let’s go back to the form component class, and we will inject this service that we have created to consume it within our component. Our constructor of the TodoFormComponent class should look like this:

Let’s not forget to import our TodoService:

import { TodoService } from ‘../services/todo.service’;

With this, we can consume the TodoService service to save our record in the Cloud FireStore, for this, we will modify the SaveTodo method with the following:

As we can see, now what we are doing is that we are extracting the object that represents the form information using todoForm.value. Then, we fill in the information of the creation date and last modified date fields, and finally, we use the todoService to save the information in Cloud Firestore. Since the SaveTodo method returns a promise, we can use then to indicate what we want to happen when Firebase finishes saving the record in the database, and of course, we can use catch to define what we want to happen in case there is an error in the operation.

If the operation is successful, then we invoke the handleSuccessfulSaveTodo function, which receives as a parameter a DocumentReference and the created task. The DocumentReference is the response that Firebase gives us about the saved operation, that object contains the ID of the registry created in Cloud Firestore. Inside handleSuccessfulSaveTodo we use the dismiss function of NgbActiveModal to close the modal and pass information to the todo-list component, specifically, we pass the newly created object and its ID.

At this time we can test our application. We can execute the project with the command:

ng serve -o

Then, we can fill the form:

createtodo

And press the Save button. With this, we have saved the record in Cloud Firestore.

If we go to our project in Firebase, and then we go to the Database section, we can see our newly created record:

primer registro

Returning to our application, we see that when the modal is closed, nothing happens, that is because we are not showing the list of tasks on the todo-list component. Let’s do this.

When the application uploads and loads the todo-list component, we want to search Cloud Firestore for all the tasks in our database and show them to the user. This task can be divided into two parts:

  • Search for the tasks in Cloud Firestore
  • Display said tasks to the user

We will start by looking for the tasks in Cloud Firestore. For that we will consume the TodoService service from our TodoListComponent. The first thing we must do is to inject the TodoService using the constructor of the todo-list.component.ts:

Now, let’s search for the list of tasks. For that we are going to implement the loadTodos method:

Here what we are doing is using the getTodos function of the TodoService to retrieve the list of tasks, since getTodos returns an observable, we use subscribe to execute the operation and receive a result.

Then, we iterate that result using forEach on the documents we receive, and in the data variable we place the data of the document, and in another variable we place the id of the document. Finally we use our TodoViewModel to store the task information, and we place this object of type TodoViewModel in the todos array.

Let’s modify the ngOnInit to invoke the loadTodos method from there:

Now, let’s go to the template of the todo-list component and we will place this code below the Add Todo button:

This code will be responsible for showing the tasks on the screen. We see that for this we use a table. On the tag of the table we see a strange attribute that says *ngIf, what is this? This is a structural directive which has the power to show or hide things depending on a certain condition. In this case, we are saying that we only want to show the table if the task array has a length greater than zero, that is, if it has any element, otherwise, we want the table not to show:

*ngIf=todos && todos.length > 0

Below, in the tr tag, we see another structural directive, *ngFor, which allows us to iterate a collection of elements, and repeat HTML code for each element of the collection. In this case, we want to write as many tr tags (representing a line in a table), as there are elements in the todos array. In our case, the expression of the *ngFor iterates the todos array. Note also that we have an index variable that allows us to have the index of a specific element in the array, this will be useful later:

*ngFor=“let todo of todos, let index = index”

Another thing we see is a ngClass. The ngClass allows us to dynamically assign classes to an HTML tag according to a certain condition. In this case, if the done field is true, we assign the CSS class “done” to the td tag:

[ngClass]=“{‘done’: todo.done}”

Let’s go to the file todo-list.component.css and paste the following code:

With this the list of tasks is shown, we can go to Google Chrome to verify this:

listado tareas

And indeed we are shown the list of tasks. However, we see that the format of the date shown in the table is not exactly user-friendly. We want the date to be displayed in a friendly way, for that we can use a pipe.

A pipe is a mechanism that helps us transform data. In our case, we will use a pipe to transform the data of the date into a more friendly format, for that we edit the cell where the date is shown in the todo-list.component.html as follows:

{{todo.lastModifiedDate | date}}

What we do is put the value we want to transform, then a vertical bar (|, alt + 124 in Windows), and finally the pipe we want to use, in this case, the pipe date. If we save and return to Google Chrome, we will see that the date is now displayed in a more friendly way.

On the screen we see that we have some buttons and a checkbox, we are going to implement the functionality associated with these buttons and the checkbox.

Let’s start with the checkbox. Let’s go back to the class of the todo-list component, and we’re going to implement the checkedDone function:

Here what we do is edit the property done of the task and then update the document in Cloud Firestore.

If we go back to Chrome we can try this functionality:

checked

Now, let’s implement the editing functionality. The first thing we will do is create two fields in the class of the todo-form component, like this:

What happens is that we will reuse the todo-form component for task editing, and for that we need to be able to discern if the user wants to create or edit a task. If you want, you can edit the modal title in the template of the todo-form component to be dynamic:

{{createMode ? ‘Create Todo’ : ‘Edit Todo’}}

We use a ternary operator so that, if the createMode variable is true, then the modal title says Create Todo, otherwise the title will be Edit Todo.

Let’s go back to the class of the todo-list component. We are going to implement the handleEditClick function:

Here we are opening the modal and we show the todo-form component. Also, notice that we change the value of createMode to false, to indicate that we will use the form in edit mode. In addition, we pass the task to edit to the component.

Let’s now implement the handleModalTodoFormClose function in the todo-list.component.ts:

This method is the one tha it is executed when the modal is closed, then if the form was in create mode, it inserts the task added to the todos array, otherwise, it looks for the task in the todos array and edits its value with the task already edited.

Let’s go back to the class of the todo-form component. Let’s create the loadTodo method and let’s call it from the ngOnInit if the createMode variable is false:

Notice that in loadTodo we use the patch method of the formGroup to edit the values of the form.

Now, let’s edit the SaveTodo method, and implement handSuccessfulSaveTodo and handleSuccessfulEditTodo:

The idea here is that now, the SaveTodo method adds or edits according to the mode our form is.

As you can see, in both cases we send the todo-list component: The task, the id of the task, and if what we did was add or edit a task. We remember that by using dismiss we close the modal, and this causes us to execute the function handleModalTodoFormClose of the class of the component todo-list.

With this we have implemented the functionality of editing. We can do a little test in Google Chrome of this if we wish.

Finally, we are going to implement the delete functionality. For that we are going to implement the handleDeleteClick function that is executed when clicking on the delete button:

As we can see, here what we do is use the deleteTodo method of the TodoService, and then, if the operation is successful then we remove the element from the todos array, otherwise we make a console.error.

With this we have all the basic functionality of our task list application: We can create tasks, display them on the screen, edit them and delete them.

However, our application has a limitation: We only have one screen. Ideally, a mechanism for the user to navigate through our app. We will learn about this in the next entry.

Summary

  • With *ngIf we can display or hide elements from the HTML dynamically
  • With *ngFor we can iterate a collection and repeat segments of HTML for each element in the collection. Ideal to display collections of values to the user

Regards!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s