Luke P
Luke P

Reputation: 734

Using duplicate VueJS component on same page, doesn't reference it's own instance properly

I have built a VueJS component and am using it multiple times on the same page. The components work independently of each other perfectly, however there's a method in the component to get it's offsetWidth and apply this (with a prefix) to the component as a class (eg width-800).

When there is only one of the component on a page, it works fine.

When there is more than one, only the last occurrence applies the class. It does calculate the class correctly with the correct width, but just doesn't apply it to the others on the page.

<template>
    <div id="app-medialib" v-bind:class="[this.breakpoint]">
        <p>{{ this.breakpoint }}</p>

this.breakpoint is a data property which holds the class name.

mounted: function(){
  var self = this;

  this.calculateBreakpoint();

  window.onresize = function(event) {
      self.calculateBreakpoint();
  };
}

and the breakpoint method:

calculateBreakpoint: function(){
    var mediaLibraryWidth = null;

    var elems = document.getElementsByClassName('header medialib');
    for( var i=0; i<elems.length; i++ ){
        if( elems[i].offsetParent !== null ){
            console.log('elems[i]', elems[i]);
            mediaLibraryWidth = elems[i].offsetWidth;
            break;
        }
    }

    console.log('calculateBreakpoint', mediaLibraryWidth);

    if( mediaLibraryWidth > 956 ) {
        this.breakpoint = 'md';
    } else if( mediaLibraryWidth < 957 && mediaLibraryWidth > 700 ){
        this.breakpoint = 'sm';
    } else {
        this.breakpoint = 'xs';
    }

    console.log('calculateBreakpoint', this.breakpoint);
},

Any help is much appreciated on this. Been scratching my head for a while now.

Thanks.

Upvotes: 1

Views: 3285

Answers (2)

imjosh
imjosh

Reputation: 4892

Apply the v-bind:class outside of the template like this

Vue.component("foo", {
  template: "<div>{{item}}</div>",
  props: ["item"]
});

var app = new Vue({
  el: "#app",
  data: {
    background: "blue",
    items: ["a", "b", "c", "d"]
  },
  mounted: function() {
    var self = this;
    this.calculateBreakpoint();
    window.onresize = function(event) {
      self.calculateBreakpoint();
    };
  },
  methods: {
    calculateBreakpoint: function() {
      var mediaLibraryWidth = null;
      var elems = document.getElementsByClassName("app");
      for (var i = 0; i < elems.length; i++) {
        if (elems[i].offsetParent !== null) {
          console.log("elems[i]", elems[i]);
          mediaLibraryWidth = elems[i].offsetWidth;
          break;
        }
      }

      console.log("calculateBreakpoint", mediaLibraryWidth);

      if (mediaLibraryWidth > 956) {
        this.background = "red";
      } else if (mediaLibraryWidth < 957 && mediaLibraryWidth > 700) {
        this.background = "blue";
      } else {
        this.background = "green";
      }

      console.log("calculateBreakpoint", this.breakpoint);
    }
  }
});
.red {
  background: red;

}

.blue {
  background: blue;

}

.green {
  background: green;

}

.widget {
  margin: 10px;
  padding: 20px;
  width: 200px;
  font-weight: bold;
  font-size: 16px;
  color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<div id="app" class="app"> 
  <foo v-for="item in items" :class="[background]" class="widget" :item="item"> 
</div> 

https://codepen.io/anon/pen/RVLrmG

Upvotes: 0

Deepak
Deepak

Reputation: 1362

When you do window.onresize and bind a function to it, the previous bindings get overridden, so when you have multiple components in your app, their mounted function is called which overrides the window.onresize. That is the reason why only the last components class is being changed.

Try the following in your console.

window.onresize = function (e) {
  console.log('a')
}

window.onresize = function (e) {
  console.log('b')
}

Only 'b' gets logged when you resize.

Checkout : What is the best practice to not to override other bound functions to window.onresize?

In your case using jquery to bind functions on window.onresize will be a better approach.

Try:

$(window).resize(function() {
 console.log('a')
})

$(window).resize(function() {
 console.log('b')
})

It will log both 'a' and 'b' when you resize.

So change your window.onresize to:

$(window).resize(function() {
  self.calculateBreakpoint();
})

Or as per Daniel Beck's suggestion, if you want to avoid using jquery, use:

 window.addEventListener('resize', self.calculateBreakpoint )

Upvotes: 3

Related Questions