Twitter LogoFacebook Logo
Angular Project 1: Building a Social Media Site Part 8: Uploading Posts
In this session, we’ll upload the post to the database so we can retrieve it later.
By: King

Hello, welcome to another video of The Complete Angular Course. 


In the last session, we created the page that will be used to display user posts and a component to create the post. 

In this session, we’ll upload the post to the database so we can retrieve it later. 

To begin, open the project from where we left off in the last session. 

Firebase Storage

Go to the Firebase Console. 


So far, we’ve used the Cloud Firestore database to upload our user data, in addition to that, now we’ll need to use the Firebase Storage database for our image files. 

Image from Codeible.com

The Files tab is where we see all the files in the Storage. 

The Rules tab shows us who and where our users can access the storage. 

The Usage tab shows us how much space and bandwidth we have used.

It is currently allowing us to read and write data to the storage if we are logged in. 

Image from Codeible.com

Since we want everyone to be able to read all the posts, remove the read parameter from the allow statement and add another allow statement to allow everyone to read. Then publish the rules.

Image from Codeible.com

Uploading Files to the Storage

Let’s see how we can add files to the storage. 


1) Go to the Create Post HTML page. 
2) Locate the textarea element and give it a template reference. 
3) Then attach a click event to the create post button and pass in the textarea reference. 

<mat-card>
    <mat-card-header>
        <mat-card-title>Create Post</mat-card-title>
    </mat-card-header>
    <div>
        <textarea #commentInput 
               placeholder="Type Message..."></textarea>
    </div>
    <div *ngIf="selectedImageFile" id="post-preview">
        <img id="post-preview-image"/>
    </div>
    <mat-card-actions align="end">
        <button mat-button color="warn">
            <label for="photo-upload">
                <input #photoSelector (change)="onPhotoSelected(photoSelector)"
                id="photo-upload" type="file" accept="image/*"/>
                <mat-icon>insert_photo</mat-icon>
            </label>
        </button>
        <button (click)="onPostClick(commentInput)" 
                mat-flat-button color="warn">Post</button>
    </mat-card-actions>
</mat-card>

1) Go to the Create Post component TypeScript file and define the function. 


2) Import the FirebaseTSAuth, FirebaseTSFirestore, and FirebaseTSStorage classes and create an object for each.

3) Inside the onPostClick function, get the value of the textarea. 

import { FirebaseTSFirestore } from 'firebasets/firebasetsFirestore/firebaseTSFirestore';
import { FirebaseTSStorage } from 'firebasets/firebasetsStorage/firebaseTSStorage';
import { FirebaseTSApp } from 'firebasets/firebasetsApp/firebaseTSApp';

auth = new FirebaseTSAuth();
firestore = new FirebaseTSFirestore();
storage = new FirebaseTSStorage();

onPostClick(commentInput: HTMLTextAreaElement) {

    let comment = commentInput.value;

}

Since Firestore cannot store files, we need upload the file to the Storage first. Once the file is in the storage, it’ll create a link for us to use to download the file. 

Instead of uploading the file directly to Firestore, we’re uploading the post information with that file URL.

To upload files to the Storage, grab the Storage object and then call the upload method. The method takes in an object with up to 6 properties but we only need 4.

1) The first one is the upload name. This allows the Storage object to keep track of how many uploads are running.

2) The second property is path, which points to the location of where we want to place the file. Unlike Firestore, the Storage do not store files in collections. It store files in folders call directories, just like on a computer. 

3) The third property is data. It is an object with 2 properties, but we’ll only need to use the one call data. In here, pass in the file that you want to upload.

4) The last property that we’ll need is the onComplete callback function. This function will get called when the file is uploaded to the Storage. An URL will be returned, and we can use it to download the file. 

onPostClick(commentInput: HTMLTextAreaElement) {

   let comment = commentInput.value;
   this.storage.upload(
      {
        uploadName: "upload Image Post",
        path: ["Posts", "Post1", "image"],
        data: {
          datathis.selectedImageFile
        },
        onComplete: (downloadUrl) => {
         
        }
      }
    );
}

Instead of placing images in a static folder call Post1, we want to place the post image in a specific folder for each post using the document id of the new post. 


To do this, declare a new variable call postId. We can generate a unique id using a method from the Firestore object call genDocId(). 

Lastly, replace Post1 with the postId.

onPostClick(commentInput: HTMLTextAreaElement) {
   
   let comment = commentInput.value;
   let postId = this.firestore.genDocId();

   this.storage.upload(
      {
        uploadName: "upload Image Post",
        path: ["Posts", postId, "image"],
        data: {
          data: this.selectedImageFile
        },
        onComplete: (downloadUrl) => {
         
        }
      }
    );
}

Add the alert statement and print the downloadUrl.

onComplete: (downloadUrl) => {
   alert(downloadUrl);
}

If we go to the app and select an image and then click on the Post button, it’ll upload the image to the Storage. 

Copy the URL from the alert and paste it in the browser. You should see the image that was uploaded. 

If we go to the Firebase Console and click on the Files tab, we will see a folder call Post. If we go inside, there is another folder, and inside that folder, is the file for our image. 

Image from Codeible.com

Storing the Post in Firestore

Let’s see how we put the information in Firestore so we can retrieve the data later. 

1) Go back to the Create Post TypeScript file. 

2) In the onComplete callback function, remove the alert statement. 

3) Grab the Firestore object and call the create method. 

For the path, put the post in a collection call Posts using the post id that was generated.
 
For the data, each post will have a comment, the id of the user who created the post, the URL for our image, and the time it was created.  

Instead of using the local time from the computer, we will use the timestamp from the server. 

4) Import the FirebaseTSApp class then call the getFirestoreTimestamp() method from the FirebaseTSApp class.

5) Lastly, add the onComplete callback function. 

import { FirebaseTSApp } from 'firebasets/firebasetsApp/firebaseTSApp';

onComplete: (downloadUrl) => {

   this.firestore.create(
      {
          path: ["Posts", postId],
          data: {
            comment: comment,
            creatorId: this.auth.getAuth().currentUser.uid,
            imageUrl: downloadUrl,
            timestamp: FirebaseTSApp.getFirestoreTimestamp()
          },
          onComplete: (docId) => {

          }
      }
    );
}

The next thing we need to do is to close the dialog when the upload is complete.


Import MatDialogRef from Angular Material and inject it in the constructor. 

For the Angle Brackets, place the name of the component. 

In the onComplete(), grab the dialogRef object and call the close() method.

import { MatDialogRef } from '@angular/material/dialog';

constructor(private dialog: MatDialogRef<CreatePostComponent>) { }

onComplete: (downloadUrl) => {

   this.firestore.create(
      {
          path: ["Posts", postId],
          data: {
            comment: comment,
            creatorId: this.auth.getAuth().currentUser.uid,
            imageUrl: downloadUrl,
            timestamp: FirebaseTSApp.getFirestoreTimestamp()
          },
          onComplete: (docId) => {
             this.dialog.close();
          }
      }
    );
}

If we run the app and create a post, the data will be uploaded to Storage and then to Firestore. 

Fixing Errors

This is great and all, but we’re not done. Right now our code only works if we have an image selected. If we do not select an image, nothing will happen. To fix this, 

1) Go to the Create Post TypeScript file and create a function call uploadImagePost(). 

2) Get the code from the onPostClick() function, and place it inside the uploadImagePost() function.

3) Lastly, add a parameter for the comment from the textarea.

uploadImagePost(comment: string){
    let postId = this.firestore.genDocId();
    this.storage.upload(
      {
        uploadName: "upload Image Post",
        path: ["Posts", postId, "image"],
        data: {
          data: this.selectedImageFile
        },
        onComplete: (downloadUrl) => {
          this.firestore.create(
            {
              path: ["Posts", postId],
              data: {
                comment: comment,
                creatorId: this.auth.getAuth().currentUser.uid,
                imageUrl: downloadUrl,
                timestamp: FirebaseTSApp.getFirestoreTimestamp()
              },
              onComplete: (docId) => {
                this.dialog.close();
              }
            }
          );
        }
      }
    );
  }

1) Create another function call uploadPost(). 


2) Go to the uploadImagePost() function and copy the code where it uploads the document to Firestore. 

3) Paste it inside the uploadPost() function. 

4) Remove the post id and the imageUrl properties. 

5) Then add a parameter for the comment as well.

uploadPost(comment: string){
    this.firestore.create(
      {
        path: ["Posts"],
        data: {
          comment: comment,
          creatorId: this.auth.getAuth().currentUser.uid,
          timestamp: FirebaseTSApp.getFirestoreTimestamp()
        },
        onComplete: (docId) => {
          this.dialog.close();
        }
      }
    );
  }

Go to the onPostClick() function and use the if statement to check if there is an selected image file. 


If there is, call the uploadImagePost() function. If not, call the uploadPost() function.

Lastly, check if the comment is empty or not. If it is, return out of the function. 

onPostClick(commentInput: HTMLTextAreaElement) {

    let comment = commentInput.value;

    if(comment.length <= 0 ) return;

    if(this.selectedImageFile) {
      this.uploadImagePost(comment);
    } else {
      this.uploadPost(comment);
    }
   
}

If we run the app and try to post an empty post, nothing will happen. 


If we enter a comment, it’ll create document in Cloud Firestore with the post information. 

If we add an image to the post, it’ll upload the file to the storage and then it’ll create a post with the added properties. 

That is all for this video, if you find this video helpful, please like, share, and subscribe to support the channel. 

In the next video, we will retrieve the posts data and display them in the post feed page. 


Sign In