Erik Martinez
Erik Martinez

Reputation: 3

Vue.js Change css style of child component on hover

I created a "box" component that I re-use several times. Each element has a @mouseenter event that the parent listens to. My goal is to change the border-color of the child element. Because I declared the from the parent with a loop I can't change only one of the childs properties but they all change

<template>
<div>
  <div id="container">
    <div id="row" v-for="i in 11" :key="i">
      <div>
        <box-component v-for="j in 7" :key="j" :color="getColor(i, j)" v-bind:borderColor="getBorder(i, j)" :row="i" :col="j" v-on:changeBorder="highlightBorder($event)"></box-component>
      </div>
    </div>
  </div>
</div>
</template>

The problem is with this part:

v-bind:borderColor="getBorder(i, j)"

Because i and j have changed I don't know how to only affect one child.

I know that I could remove the loop and copy paste the same code but there must be another solution to this. I also know that this particular example could be implemented directly on the child component but I need to be able to do it from the parent.

Upvotes: 0

Views: 3720

Answers (1)

blex
blex

Reputation: 25634

You can do it this way:

<box-component v-on:change-border="highlightBorder(i, j)"></box-component>

From the docs:

Unlike components and props, event names will never be used as variable or property names in JavaScript, so there’s no reason to use camelCase or PascalCase. Additionally, v-on event listeners inside DOM templates will be automatically transformed to lowercase (due to HTML’s case-insensitivity), so v-on:myEvent would become v-on:myevent – making myEvent impossible to listen to.

For these reasons, we recommend you always use kebab-case for event names.

Interactive demo

Vue.component('parent-component', {
  template: '#parent-component',
  data() {
    return {
      defaultStyles: {
        color: '#555',
        borderColor: '#bbb'
      },
      highlightedStyles: {
        color: '#f50',
        borderColor: 'orange'
      },
      highlighted: {x: null, y: null}
    };
  },
  methods: {
    isHighlighted(x, y) {
      return x === this.highlighted.x && y === this.highlighted.y;
    },
    getStyles(x, y) {
      return this.isHighlighted(x, y) ? this.highlightedStyles : this.defaultStyles;
    },
    getColor(x, y) {
      return this.getStyles(x, y).color;
    },
    getBorder(x, y) {
      return this.getStyles(x, y).borderColor;
    },
    highlightBorder(x, y) {
      this.highlighted = {x, y};
    }
  }
});

Vue.component('box-component', {
  template: '#box-component',
  props: ['color', 'borderColor']
});


var vm = new Vue({
  el: '#app'
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.row:after {
  content: '';
  display: block;
  clear: both;
}

.box {
  float: left;
  padding: .5em;
  border-width: 4px;
  border-style: solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<div id="app">
  <parent-component></parent-component>
</div>

<template id="parent-component">
  <div>
    <div id="container">
      <div class="row" v-for="y in 11" :key="`row-${y}`">
        <div>
          <box-component
            v-for="x in 7"
            :key="`cell-${x}`"
            :color="getColor(x, y)"
            :border-color="getBorder(x, y)"
            :col="x" :row="y"
            @change-border="highlightBorder(x, y)"
          ></box-component>
        </div>
      </div>
    </div>
  </div>
</template>

<template id="box-component">
  <div
    class="box"
    :style="{background: color, borderColor: borderColor}"
    @mouseenter="$emit('change-border')"
  ></div>
</template>

Upvotes: 2

Related Questions