0
votes

I am still getting the hang of Vue and I need to do something here that is escaping me. I have a WS.vue component that binds a class to a div (active) by the click of a checkbox. At the moment, the click works. It changes the class independently to each call of this reusable component. I want to take this a little further as to control which instance of the WS components start active and which do not. The toggle should still be able to turn off the active if it loads active and vice versa.

Here is the WS component:

<template>
  <div class="ws" :class="{active: active}">
    <span class="ws-content">
      <label class="uk-switchl" uk-toggle="target: #toggleSwitch">
        <input type="checkbox" id="toggleSwitch" @change="active = !active" />
        <div class="uk-switch-slider uk-switch-small"></div>
      </label>
    </span>
  </div>
</template>

<script>
export default {
  name: "ws",
  props: {
    active: false
  },
  data() {
    return {
      // active: false,     not necessary I believe
    };
  }
};
</script>

The parent component that will use them looks like this now

<WS agency1="Boston" />
<WS agency1="Boston" />
<WS agency1="Phildelphia" />
...
...
<WS agency1="Miami" />

I'd like to use them like this

<WS agency1="Boston" />
<WS agency1="Phildelphia" active/>

or

<WS agency1="Boston" active="false" />
<WS agency1="Phildelphia" active="true"/>

That way I can bind that active attribute to the API and control it by a boolean value

Does that make sense?

1

1 Answers

4
votes

The short answer is that you should keep your state entirely in the parent component. Simply emit an event when the checkbox is changed and inform the parent when to alter its state.

You could implement the ws component like this:

export default {
  props: {
    active: {
      type: Boolean,
      required: true,
    },
  },
  methods: {
    onChange() {
      this.$emit('toggle');
    },
  },
};

Note:

  • We have removed data
  • You'll need to call onChange from your template. Replace @change="active = !active" with @change="onChange".

The parent component should declare an an array of ws data like so:

data() {
  return {
    wsData: [
      { name: 'Boston', active: false },
      { name: 'Seattle', active: false },
      { name: 'San Francisco', active: true },
      { name: 'New York', active: false },
    ],
  };
},

The parent template could render the set of ws components like this:

<ul>
  <li v-for="(ws, index) in wsData" :key="index">
    <WS :agency="ws.name" :active="ws.active" @toggle="toggleState(index)" />
  </li>
</ul>

Finally you'll add a toggleState method like this:

methods: {
  toggleState(index) {
    this.wsData[index].active = !this.wsData[index].active;
  }
}

As an aside, I suggest using eslint-plugin-vue, and crank it up to plugin:vue/recommended. This will prevent you from doing things like declaring a data key that shadows one of your props.