Search code examples
functionvue.jsrendering

VueJS rendering after a function finish


So I've got this problem: I got this component in VueJS:

<img v-bind:src="'data:image/jpg;base64,'+ filesData[posts[file.index].id]" class="image" />

This is inside a for instruction

<div v-for="file in files" :key="file.name" style="width: 30%; height: auto;">

So everything is fine here. Inside the same for I've got a function used to supply filesData array with the correct data. The function is the following:

getEachFile: function(id){
  console.log(id)
  setTimeout(function(){}, 500);
  this.axios({
    method: 'get',
    url: 'http://' + this.$store.state.server + ':3000/post/' + id,
    headers:{
      'authorization': this.$store.state.token
    }
  }).then((response) => {
      var bytes = response.data.data.data;
      this.filesData[id] = this.encode(new Uint8Array(bytes), id);
  })
}

So I'm sending an HTTP request to my local server. The local serve is returning a buffer that contains data of an image. You can see that in the response promise I call another function This is that function(used to convert to base64)

encode: function(input, id) {
  var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  var output = "";
  var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
  var i = 0;

  while (i < input.length) {
      chr1 = input[i++];
      chr2 = i < input.length ? input[i++] : Number.NaN; // Not sure if the index 
      chr3 = i < input.length ? input[i++] : Number.NaN; // checks are needed here

      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;

      if (isNaN(chr2)) {
          enc3 = enc4 = 64;
      } else if (isNaN(chr3)) {
          enc4 = 64;
      }
      output += keyStr.charAt(enc1) + keyStr.charAt(enc2) +
                keyStr.charAt(enc3) + keyStr.charAt(enc4);
  }
  return output;
}

Every thing is just fine but the problem is that the 'img' component/tag isn't rendering again after the function encode finish to execute. As I searched on internet, I've realized that using something like:

this.filesData[id] = 'something'

is not forcing the component to re-render, and I have to use that Vue.set() But using Vue.set(), forcing to re-render, creates an infinit loop, the function call beeing inside the 'for' instruction. Is there any way to set the filesData array with these values and after the values are ready to re-render only the 'img' component? If not, what could I do to render these tags after the function execute?

Updated: I've put here the for instruction entirely

<!--"For" used to display 6 posts. Files variable stores the files from HTTP response.-->
<div v-for="file in files" :key="file.name" style="width: 30%; height: auto;">

  <div>{{getEachFile(posts[file.index].id)}}</div>
  <!--Each post is displayed using a dialog template, so when you click one of the images, a dialog box is open.-->

  <v-dialog
    v-model="dialog[file.index]"
    height="auto"
    max-width="800"
  >
    <!--Here starts the activator for the dialog box. The activator si the entire post.-->
    <template v-slot:activator="{ on, attrs }">
      <!--The following is the entire flex item described by the css from App.vue-->
      <div class="flexitem"
           v-bind="attrs"
           v-on="on"
      >
        <postName :file="file" :posts="posts"/>
        <description :file="file" :posts="posts"/>
        <img v-bind:src="'data:image/jpg;base64,'+ filesData[posts[file.index].id]" class="image" />
      </div>
    </template>

    <dialogBox :file="file" :posts="posts" :filesData="filesData"/>
  </v-dialog>
</div>

Solution

  • This is happening because you are not updating the actual file object within the files array. If that would be the case the component would re-render properly as you have assigned a unique key prop to each of them.

    In that case the best solution is to just use forceUpdate. You can call it globally (Vue.forceUpdate()) or locally as follows:

    getEachFile: function(id){
      console.log(id)
      setTimeout(function(){}, 500);
      this.axios({
        method: 'get',
        url: 'http://' + this.$store.state.server + ':3000/post/' + id,
        headers:{
          'authorization': this.$store.state.token
        }
      }).then((response) => {
          var bytes = response.data.data.data;
          this.filesData[id] = this.encode(new Uint8Array(bytes), id);
          this.$forceUpdate(); // calling it after encode 
      })
    }
    

    There's a medium post about forcing components to re-render. I think it could be helpful.