vue.jsvuejs3bootstrap-5grid-layoutmasonry

How to update Masonry on Vue.js 3 updated() lifecycle hook?


I have a question regarding the Masonry layout update. How can I trigger it in the Vue.js (Version 3) lifecycle hook "updated()"?

I have a card grid view like in the following code (using Bootstrap 5 here), but struggle to update the Masonry layout when e.g. a card changes its size. I know that I can use layout() see documentation but I don't know how to call it in my example.

So my main problem is, that I don't know how to make my Masonry object accessible in the updated function when initialized in the mounted() function. How do I do that?

<template>
  <div
    class="row justify-content-left"
    data-masonry='{"percentPosition": true }'
  >
    <div class="col-md-6 col-lg-4 col-xxl-3 mb-4 card-col">
      <Card 1 />
    </div>
    <div class="col-md-6 col-lg-4 col-xxl-3 mb-4 card-col">
      <Card 2 />
    </div>
    <div class="col-md-6 col-lg-4 col-xxl-3 mb-4 card-col">
      <Card 3 />
    </div>
    <div class="col-md-6 col-lg-4 col-xxl-3 mb-4 card-col">
      <Card 4 />
    </div>
  </div>
</template>

<script>
[imports ...]

export default {
  name: "ComponentName",
  components: {
    [components ...],
  },
  mounted: function () {
    // initialize masonry
    this.$nextTick(function () {
      var row = document.querySelector("[data-masonry]");
      new Masonry(row, {
        // options
        percentPosition: true,
      });
    });
  },
  updated: function() {
    //-->how to call the relayout of Masonry here?
  }
};
</script>

Solution

  • To cache a reference the resulting Masonry instance, just assign it to a property (you don't need reactivity on this instance, so attaching a property is fine):

    export default {
      mounted() {
        this._masonry = new Masonry(this.$refs.masonry, ⋯)
      },
      updated() {
        this._masonry.layout()
      }
    }
    

    However, you'll notice that the this._masonry.layout() call does not actually re-layout the masonry items (likely due to some limitation of the library). The only workaround I see is to recreate the Masonry instance, and then you no longer need to cache a reference to it:

    export default {
      mounted() {
        this.$nextTick(() => this.layout())
      },
      updated() {
        this.layout()
      },
      methods: {
        layout() {
          new Masonry(this.$refs.masonry, ⋯)
        }
      }
    }
    

    demo