17

I know how to use masonry.js apart from vue. However, I'm having issue getting it to function and be called correctly inside of the vue framework. I called it inside of the created or ready but neither seem to get the grid to form correctly. How can i get this to work inside the framework? Oh and I do have jquery called in the html before this script. Here is what I have inside the component :

Edit:

I can see that the masonry is effecting the grid by assigning its height with JS and changing the items to position absolute. However, its not placing them correctly. Its stacking them ontop eachother instead of sideby side like it should be in the grid. enter image description here

<template>
  <div class="projects--container">
    <div class="inner-section inner--options">
        <div class="grid">
            <div class="grid-item"></div>
            <div class="grid-item"></div>
            <div class="grid-item"></div>
        </div>
    </div>
  </div>
</template>

<script>
 export default{
    ready: function () {
        this.mason();
    },
     data: function () {
         return {
             options: [
                 {
                   option: 'projects',
                     phrase: 'for clients',
                     slogan: 'slogan...'
                 },
                 {
                     option: 'sides',
                     phrase: 'for us',
                     slogan: 'we love what we make'
                 },
                 {
                     option: 'moments',
                     phrase: 'with the crew'
                 }
             ]
         }
     },
     methods: {
         revert: function () {
             this.$dispatch('return-home', true)
         },
         mason: function () {
             var $grid = $('.grid').masonry({
                 itemSelector: '.grid-item',
                 columnWidth: 250
             });
             $grid.masonry('layout');
         }
     },
     events: {
         'option-select': function (option) {
         }
     }

 }
</script>
Minal Chauhan
  • 5,739
  • 5
  • 15
  • 36
Ricki Moore
  • 1,093
  • 4
  • 23
  • 44

5 Answers5

8

I guess the vue-way of doing this is by using refs. Just assign a ref property to your html element inside the template and access it using the vm.$ref instance property inside the mounted callback.

A sample code may look like this:

<template>
  <div class="grid" ref="grid">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
  </div>
</template>

<script>
  import Masonry from "masonry"; // or maybe use global scoped variable here
  export default {
    mounted: function(){
      let $masonry = new Masonry(this.$refs.grid, {
        // masonry options go in here
       // see https://masonry.desandro.com/#initialize-with-vanilla-javascript
      });
    }
  }
</script>
riyaz-ali
  • 6,509
  • 2
  • 18
  • 33
7

In Vue2, there is no such thing as a ready lifecycle hook. Instead, the mounted lifecycle hook is triggered once the instance is "ready" in the way you think of.

Reference: https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram

Rashad Saleh
  • 2,023
  • 17
  • 23
7

As I saw it, most mv* frameworks like vue keep DOM elements (view) in sync with js (model), in the other hand, frameworks like masonry just need valid DOM to work. So, the tricky part is to tell one to another when DOM has changed.

So the first change is when vue finished to render all DOM, as mentioned in other answers, we are notified on mounted lifecycle hook, here is where we can initialize masonry

mounted() {
    let grid = document.querySelector('.grid');
    this.msnry = new Masonry(grid, {
        columnWidth: 25
    });
},

In any other change to our view need also update masonry as well, if you change items size use layout() method, if you add or remove items use reloadItems() method

    methods: {
        toggle(item) {
            item.isGigante = !item.isGigante;
            Vue.nextTick(() => {
                // DOM updated
                this.msnry.layout();
            });
        },
        add() {
            this.items.push({
                isGigante: false,
                size: '' + widthClasses[Math.floor(Math.random() * widthClasses.length)] + ' ' + heightClasses[Math.floor(Math.random() * heightClasses.length)]
            });
            Vue.nextTick(() => {
                // DOM updated
                this.msnry.reloadItems();
                this.msnry.layout();
            });
        }
    }

Please note that those methods are called after vue has completed DOM update using Vue.nextTick function. Here is a working fiddle.

AldoRomo88
  • 1,886
  • 1
  • 16
  • 19
0

It could be that the vertical stack just indicates that masonry is not working (It's hard to tell without a codepen/plunkr). @riyaz-ali has the right idea though.

evanfuture
  • 1
  • 1
  • 1
0

you must call masonry inside mounted() event to make it work. i am using this on my project (with imagesloaded) its work perfectly

massonryApply (container, context, selector) {
  container = $(`#${container}`)
  const $grid = container.imagesLoaded(function () {
    $grid.masonry({
      itemSelector: `.${selector}`,
      percentPosition: true,
      columnWidth: `.${selector}`
    })

    $grid.masonry('reloadItems')
  })
}
cahyowhy
  • 494
  • 1
  • 7
  • 24