Search code examples
firebasefirebase-realtime-databasevue.jsfirebase-storagevuefire

Vuefire get Firebase Image Url


I am storing relative paths to images in my firebase database for each item I wish to display. I am having trouble getting the images to appear on the screen, as I need to get the images asynchronously. The firebase schema is currently as follows:

{
  items: {
    <id#1>: {
      image_loc: ...,
    },
    <id#2>: {
      image_loc: ...,
    },
  }
}

I would like to display each of these images on my page with code such as:

<div v-for="item in items">
  <img v-bind:src="item.image_loc">
</div>

This does not work, as my relative location points to a place in firebase storage. The relavent code to get the true url from this relative url is:

firebase.storage().ref('items').child(<the_image_loc>).getDownloadURL()

which returns a promise with the true url. Here is my current vue.js code:

var vue = new Vue({
  el: '.barba-container',
  data: {
    items: []
  },
  firebase: function() {
    return {
      items: firebase.database().ref().child('items'),
    };
  }
});

I have tried using computed properties, including the use of vue-async-computed, but these solutions do not seem to work as I cannot pass in parameters.

Basically, how do I display a list of elements where each element needs the result of a promise?


Solution

  • I was able to solve this by using the asyncComputed library for vue.js and by making a promise to download all images at once, instead of trying to do so individually.

    /**
     * Returns a promise that resolves when an item has all async properties set
     */
    function VotingItem(item) {
      var promise = new Promise(function(resolve, reject) {
        item.short_description = item.description.slice(0, 140).concat('...');
    
        if (item.image_loc === undefined) {
          resolve(item);
        }
        firebase.storage().ref("items").child(item.image_loc).getDownloadURL()
          .then(function(url) {
            item.image_url = url;
            resolve(item); 
          })
          .catch(function(error) {
            item.image_url = "https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150";
            resolve(item);
          });   
      });  
      return promise;
    }
    
    var vue = new Vue({
      el: '.barba-container',
      data: {
        items: [],
        is_loading: false
      },
      firebase: function() {
        return {
          items: firebase.database().ref().child('items'),
        };
      },
      asyncComputed: {
        processedItems: {
          get: function() {
            var promises = this.items.map(VotingItem);
            return Promise.all(promises);
          },
          default: []
        }
      }
    });
    

    Lastly, I needed to use: v-for="item in processedItems" in my template to render the items with image urls attached