1

Consider this:

  • An API loads a manifest of image metadata. The images have an ID, and with another API call returns a base64 image from the DB. The model for the manifest is attachmentRecord and the ID is simply a field.
  • I would rather not preload these large strings into an array (that would work).

so I have this (which lazy loads on any manifest change):

<div v-for="(attachment, index) in  attachmentRecord" :key="index">
    <img :src="fetchImage(attachment.id)" />
</div> 

fetchimage() is a wrapper for an axios function which returns back from a promise. (writing this from memory):

this.axios({
    method: "get",
    url: url,
 }).then(res => res.data)
   .catch(() => {
      alert("Unable to load raw attachment from this task and ID");
    });
 }

Now, the network calls go thru fine, the ID passes in correctly, I can see the base 64data, but they don't seem to make it to wrapper function or the src attribute. It always comes up blank. I tried wrapping it in another promise,only to get a promise back to the src attribute. What would be a best practice for this situation in Vue?

Ok, so far I made these changes with Constantin's help: I tried to strip it down without a helper function:

Vue template Code:

<div v-for="(attachment, index) in  attachmentRecord" :key="index">
<img :src="getAttachmentFromTask(attachment.id)" />

base method:

async getAttachmentFromTask(attachmentID) {
  if (!attachmentID) alert("Unknown Attachment!");
  let sendBack = "";
  let url = "/server/..."

  await this.axios({
    method: "get",
    url: url
  })
    .then(res => {
      sendBack = res.data;
    })
    .catch(() => {
      alert("Unable to load raw attachment from this task and ID");
    });
  // >>>>>>>>>alerts base64 correctly; Vue loads [object Promise] in img
  alert(sendBack);
  return sendBack; 
}

1 Answer 1

1

It turns out that Vue doesn't handle async / await as well as I thought. Therefore, you have to save the image data to each attachment in attachmentRecord. This getAttachmentFromTask method now handles this when accessed the first time and populates a data property for the corresponding attachment object. On successive calls, that property is returned if it is already populated. Note the usage of Vue.set() because the property is not available in the initial data, but we want it to be reactive. You can even set up a fallback image like a loader, see the shortly flickering SO logo without text before the larger logo appears:

new Vue({
  el: '#app',
  data: {
    attachmentRecord: [{
      id: 1
    }]
  },
  methods: {
    getAttachmentFromTask(attachmentIndex, attachmentID) {
      let record = this.attachmentRecord[attachmentIndex];
      if (!record.data) {
        Vue.set(record, 'data', null);
        axios.get('https://kunden.48design.de/stackoverflow/image-base64-api-mockup.json').then((result) => {
          Vue.set(record, 'data', result.data);
        });
      }
      return this.attachmentRecord[attachmentIndex].data;
    }
  }
});
img {
  max-width: 100vw;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
  <div v-for="(attachment, index) in  attachmentRecord" :key="index">
    <img :src="getAttachmentFromTask(index, attachment.id) || 'https://cdn.sstatic.net/Sites/stackoverflow/img/apple-touch-icon.png'" />
  </div>
</div>

old answer: (Unfortunately doesn't work that way with Vue currently)

Axios requests are asynchronous by default. So the function doesn't wait for then() to return the value. You could add the async keyword before your fetchImage function name and add the await keyword before this.axios. Then make the then callback assign the return value to a variable in the fetchImage function scope and have the function return it.

async fetchImage() {
    let returnValue;
    await this.axios({
        method: "get",
        url: url,
     }).then(res => { returnValue = res.data; })
       .catch(() => {
          alert("Unable to load raw attachment from this task and ID");
        });
    return returnValue;
} 
Sign up to request clarification or add additional context in comments.

13 Comments

Thanks! This solved part of it actually. I will mark as solved. I still can't get the src attribute to update. I can break in the then() call, return a value, but nothing is returned to src attribute. All I (fetchimage changed to getSrcImage) getSrcImage: function(id) { let that = this; that.getAttachmentFromTask(id).then(str => { return str; }); also tried: function(id) { let that = this; let buffer = ""; that.getAttachmentFromTask(id).then(str => { buffer = str; }); return buffer; }
You're missing the async/await again!
getSrcImage: async function(id) { let returnValue; await this.getAttachmentFromTask(id).then(str => { returnValue = str; }); return returnValue; } - getAttachmentFromTask's definition will have to have the async keyword as well.
src= [object Promise] when I change to getSrcImage: async function(id) { let that = this; let buffer = ""; await that.getAttachmentFromTask(id).then(str => { buffer = str; }); return buffer; } I had this issue when I experimented before. Something simple?
Dankeschön! I'm aware of SET, it is usually for making some array operations reactive. I marked it as answered, but it essentially caching the strings.I guess that is a limitation in vue.I also understand async/await now perfectly!
|

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.