admin管理员组

文章数量:1023204

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 es 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; 
}

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 es 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; 
}
Share Improve this question edited Nov 3, 2019 at 20:36 Len asked Nov 3, 2019 at 16:32 LenLen 5421 gold badge5 silver badges11 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 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./ajax/libs/axios/0.19.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare./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/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;
} 

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 es 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; 
}

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 es 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; 
}
Share Improve this question edited Nov 3, 2019 at 20:36 Len asked Nov 3, 2019 at 16:32 LenLen 5421 gold badge5 silver badges11 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 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./ajax/libs/axios/0.19.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare./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/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;
} 

本文标签: javascriptLoading images in vuejs from an APIStack Overflow