1

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?

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

Comments

0

I was able to solve it without any extra dependencies not adding elements to the array until the url is resolved:

in my template:

<div v-for="foo in foos" :key="foo.bar">
  <img :src="foo.src" :alt="foo.anotherbar">
  ...
</div>

in my component (for example inside mounted())

const db = firebase.firestore()
const storage = firebase.storage().ref()
const _this = this

db.collection('foos').get().then((querySnapshot) => {
  const foos = []
  querySnapshot.forEach((doc) => {
    foos.push(doc.data())
  })
  return Promise.all(foos.map(foo => {
    return storage.child(foo.imagePath).getDownloadURL().then(url => {
      foo.src = url
       _this.foos.push(foo)
    })
  }))
}).then(() => {
  console.log('all loaded')    
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.