Search code examples
angularfirebasegoogle-cloud-firestorefirebase-storageionic5

unable to access variable from inside getDownloadUrl() after uploading to AngularFireStore


I have already successfully uploaded images into my firebase storage, but I also want to retrieve the download URL and add it to my firestore database. Console logging "URL" returns me the link I want. But I am having trouble using it.

I have tried this.profileImage = URL but the console will always return me error Cannot set property 'profileImage' of undefined. I have it defined by writingprofileImage above the constructor.

I have also tried placing the entire firestore function inside but the console will return cannot read property 'firestore' of undefined. I am using Ionic 5.

imageRef.getDownloadURL().then((url)=> {
    this.firestore.collection('users').doc(this.user.id).update({image.url})
       console.log("this is my image" + url)
})

this is what I currently have

uploadImage(imageURI) {
    return new Promise<any>((resolve, reject) => {
      let storageRef = firebase.storage().ref();
      const imageRef = storageRef.child('image').child(this.createFileName());
      this.encodeImageUri(imageURI, function (image64) {
        imageRef.putString(image64, 'data_url')
          .then(function (snapshot) {
            console.log(snapshot)
            resolve(snapshot.downloadURL)
            imageRef.getDownloadURL().then((url)=> {
              this.profileImage = url
              console.log(this.profileImage)
              console.log("this is my image" + url)
            })
          }, err => {
            reject(err);
          })
      })
    })
  }
encodeImageUri(imageUri, callback) {
    var c = document.createElement('canvas');
    var ctx = c.getContext("2d");
    var img = new Image();
    img.onload = function () {
      var aux: any = this;
      c.width = aux.width;
      c.height = aux.height;
      ctx.drawImage(img, 0, 0);
      var dataURL = c.toDataURL("image/jpeg");
      callback(dataURL);
    };
    img.src = imageUri;
  };

Solution

  • If you are using a "normal" function instead of an arrow function you lose the "this context".

    Difference between an arrow function and a "normal" function: https://stackoverflow.com/a/34361380/11592273

    uploadImage(imageURI) {
        return new Promise<any>((resolve, reject) => {
          let storageRef = firebase.storage().ref();
          const imageRef = storageRef.child('image').child(this.createFileName());
            // replaced with an arrow function
          this.encodeImageUri(imageURI, (image64) => {
            imageRef.putString(image64, 'data_url')
                 // replaced with an arrow function
              .then((snapshot) => {
                console.log(snapshot)
                imageRef.getDownloadURL().then((url)=> {
                  this.profileImage = url
                  console.log(this.profileImage)
                  console.log("this is my image" + url)
                    // call the resolve method after all async tasks are complteded.
                  resolve(snapshot.downloadURL)
                })
              }, err => {
                reject(err);
              })
          })
        })
      }
    

    Also, I would recommend using await/async instead of then/catch.

    async uploadImage(imageURI) {
      const storageRef = firebase.storage().ref();
      const imageRef = storageRef.child('image').child(this.createFileName());
      const image64 = await this.encodeImageUri(imageURI);
      const snapshot = await imageRef.putString(image64, 'data_url');
      const url = imageRef.getDownloadURL();
      this.profileImage = url;
      return snapshot.downloadURL;
    }