[Vuejs]-How can I bind two classes on two distinct conditions in Vue.js?

1πŸ‘

βœ…

When you are binding the class it uses a javascript object where you can have multiple properties.

That means that you can assign multiple classes:

<li v-for="(step, index) in steps" v-bind:class="{ active: index + 1 === stepCounter, completed : index < stepCounter  }">{{step.text}}</li>

Class binding

var app = new Vue({
  el: "#cart",
  data: {
    stepCounter: 1,
    steps: [
      { step: 1, completed: false, text: "Shopping cart" },
      { step: 2, completed: false, text: "Shipping" },
      { step: 3, completed: false, text: "Payment" },
      { step: 4, completed: false, text: "Confirmation" }
    ]
  },
  mounted() {},
  methods: {
    doPrev: function() {
      if (this.stepCounter > 1) {
        this.stepCounter--;
      }
    },
    doNext: function() {
      if (this.stepCounter < this.steps.length) {
        this.stepCounter++;
        this.doCompleted();
      }
    },
    doCompleted: function() {
      this.steps.forEach(item => {
        if(item.step < this.stepCounter){
          item.completed = true;
        }
      });
    }  
  }
});
* {
      margin: 0;
      padding: 0;
      font-family: "Poppins", sans-serif;
    }

    .progressbar {
      display: flex;
      list-style-type: none;
      counter-reset: steps;
      padding-top: 50px;
      justify-content: space-between;
    }

    .progressbar li {
      font-size: 13px;
      text-align: center;
      position: relative;
      flex-grow: 1;
      flex-basis: 0;
      color: rgba(0, 0, 0, 0.5);
      font-weight: 600;
    }

    .progressbar li.completed {
      color: #ccc;
    }

    .progressbar li.active {
      color: #4caf50;
    }

    .progressbar li::after {
      counter-increment: steps;
      content: counter(steps, decimal);
      display: block;
      width: 30px;
      height: 30px;
      line-height: 30px;
      border: 2px solid rgba(0, 0, 0, 0.5);
      background: #fff;
      border-radius: 50%;
      position: absolute;
      left: 50%;
      margin-left: -15px;
      margin-top: -60px;
    }

    .progressbar li.active::after,
    .progressbar li.completed::after {
      background: #4caf50;
      border-color: rgba(0, 0, 0, 0.15);
      color: #fff;
    }

    .progressbar li.completed::after {
      content: '\2713';
    }

    .progressbar li::before {
      content: "";
      position: absolute;
      top: -26px;
      left: -50%;
      width: 100%;
      height: 2px;
      background: rgba(0, 0, 0, 0.5);
      z-index: -1;
    }

    .progressbar li.active::before,
    .progressbar li.completed::before,
    .progressbar li.active+li::before {
      background: #4caf50;
    }

    .progressbar li:first-child::before {
      display: none;
    }
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>

<div id="cart">
  <div class="container">
    <ul class="progressbar">
      <li v-for="(step, index) in steps" v-bind:class="{active: index + 1 === stepCounter, completed : index < stepCounter  }">{{step.text}}</li>
    </ul>
  </div>

  <div class="container mt-5 text-center">
    <div class="btn-group">
      <button type="button" class="btn btn-sm btn-success" @click="doPrev()">Previous</button>
      <button type="button" class="btn btn-sm btn-success" @click="doNext()">Next</button>
    </div>
  </div>
</div>
πŸ‘€Pierre Said

1πŸ‘

you can add more classes to the condition you need by using an array on the bind, like this:

<li v-for="(step, index) in steps" v-bind:class="[{completed: index < stepCounter-1},{active: index + 1 === stepCounter}]">{{step.text}}</li>
πŸ‘€Ric.H

1πŸ‘

please see amended below code, you can also use a method to generate the class based on the arguments this keeps the markup a little cleaner and a central method which generates the correct classes.

var app = new Vue({
  el: "#cart",
  data: {
    stepCounter: 1,
    steps: [
      { step: 1, completed: false, text: "Shopping cart" },
      { step: 2, completed: false, text: "Shipping" },
      { step: 3, completed: false, text: "Payment" },
      { step: 4, completed: false, text: "Confirmation" }
    ]
  },
  mounted() {},
  methods: {
    doPrev: function() {
      if (this.stepCounter > 1) {
        this.stepCounter--;
      }
    },
    doNext: function() {
      if (this.stepCounter < this.steps.length) {
        this.stepCounter++;
        this.doCompleted();
      }
    },
    doCompleted: function() {
      this.steps.forEach(item => {
        if(item.step < this.stepCounter){
          item.completed = true;
        }
      });
    },
    getClass: function(index, step) {
      var values = [];
      if (index + 1 === this.stepCounter) values.push('active');
      if (step.completed) values.push('completed');
      return values.join(' ');
    }
  }
});
* {
      margin: 0;
      padding: 0;
      font-family: "Poppins", sans-serif;
    }

    .progressbar {
      display: flex;
      list-style-type: none;
      counter-reset: steps;
      padding-top: 50px;
      justify-content: space-between;
    }

    .progressbar li {
      font-size: 13px;
      text-align: center;
      position: relative;
      flex-grow: 1;
      flex-basis: 0;
      color: rgba(0, 0, 0, 0.5);
      font-weight: 600;
    }

    .progressbar li.completed {
      color: #ccc;
    }

    .progressbar li.active {
      color: #4caf50;
    }

    .progressbar li::after {
      counter-increment: steps;
      content: counter(steps, decimal);
      display: block;
      width: 30px;
      height: 30px;
      line-height: 30px;
      border: 2px solid rgba(0, 0, 0, 0.5);
      background: #fff;
      border-radius: 50%;
      position: absolute;
      left: 50%;
      margin-left: -15px;
      margin-top: -60px;
    }

    .progressbar li.active::after,
    .progressbar li.completed::after {
      background: #4caf50;
      border-color: rgba(0, 0, 0, 0.15);
      color: #fff;
    }

    .progressbar li.completed::after {
      content: '\2713';
    }

    .progressbar li::before {
      content: "";
      position: absolute;
      top: -26px;
      left: -50%;
      width: 100%;
      height: 2px;
      background: rgba(0, 0, 0, 0.5);
      z-index: -1;
    }

    .progressbar li.active::before,
    .progressbar li.completed::before,
    .progressbar li.active+li::before {
      background: #4caf50;
    }

    .progressbar li:first-child::before {
      display: none;
    }
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>

<div id="cart">
  <div class="container">
    <ul class="progressbar">
      <li v-for="(step, index) in steps" v-bind:class="getClass(index, step)">{{step.text}}</li>
    </ul>
  </div>

  <div class="container mt-5 text-center">
    <div class="btn-group">
      <button type="button" class="btn btn-sm btn-success" @click="doPrev()">Previous</button>
      <button type="button" class="btn btn-sm btn-success" @click="doNext()">Next</button>
    </div>
  </div>
</div>
πŸ‘€Ginger Wizard

Leave a comment