Is there a way to connect two classes based on two separate conditions in Vue.js?

I am currently developing a compact checkout stepper using Vue version 2.x.x.

The item that is actively being processed should be marked with the class name "active", while all previous items should have the class name "completed", as depicted below:



  • Shopping cart
  • Shipping
  • Payment
  • Confirmation

The challenge

In Vue, I managed to add the "active" class using v-bind:class. However, I encountered difficulty in adding the "completed" class based on specific conditions for each element. Here's an example snippet showcasing my issue:

Vue code block

If you have any suggestions or solutions to help me address this problem, I would appreciate your input. Thank you!

Answer №1

When utilizing class binding, you are working with a JavaScript object that can contain multiple properties.

This allows you to assign various classes:

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

Learn more about class binding here

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>

Answer №2

To include additional classes based on a certain condition, you can utilize an array within the binding function. Here's an example:

<li v-for="(item, index) in items" v-bind:class="[{highlighted: index > itemCounter + 5},{activated: index === itemCounter}]">{{item.name}}</li>

Answer №3

Kindly review the modified code snippet below. Consider implementing a method to dynamically generate classes based on the arguments provided, which can enhance readability and maintain consistency throughout the markup.

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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Combine Immer and NgRx reducer for improved state management

Upon analyzing redux and ngrx, it appears that immer is the preferred library for creating a copy of the state before storing it. In following the example provided by immer, I implemented the following code in my reducer: on(exampleActions.updateExample ...

I am unable to deliver an email to the mailbox

Whenever I try to send an email, I encounter an issue. In order to complete the registration process, a confirmation mail needs to be sent. The error message that I receive is: Callback must be a function at maybeCallback const fs = require(& ...

What is the best way to modify the size of a canvas element while maintaining effectiveness?

I've encountered an issue while using Canvas to create a pie chart with chart.js. Despite adjusting the dimensions of the canvas element, it continues to take up the entire page. <canvas id="myChart" height ="200" width="200"></can ...

Having trouble with Laracast Video(Push Events to the Client) error?

After following a video tutorial on event handling, everything was working perfectly until the last minute when I tried to display the user name in real time on a list view. Although I could see each object in my console using console.log('users' ...

Nuxt allows you to open a component or page as a modal with its own unique URL, similar to the

I am developing a Nuxt application with a list of properties, and I want to create a functionality where clicking on a property opens the corresponding property page (/properties/_slug.vue) as a modal in front of the current page. dribble.com serves as a ...

Sending a result back to a Silverlight user control from a slightly intricate JavaScript function

I am working on a Silverlight user control that contains a textbox and a button. Within this Silverlight page, there can be multiple instances of these user controls. My goal is to have a JavaScript function trigger when the button is clicked. This functi ...

Troubarked by problems NodeJS faces when trying to establish a connection with CosmosDB using a connection

Having trouble with my code that fails when I try to create a new instance of the CosmosClient. The option to create a CosmosClient using a connection string should be straightforward. The environment variable holds the necessary connection string in this ...

What Occurs to Processed Messages in Azure Functions if Result is Not Output?

I have created an Azure function that is connected to the messaging endpoint of an IoT Hub in order to trigger the function for all incoming messages. The main purpose of this function is to decompress previously compressed messages using GZIP before they ...

javascript: unable to modify currentTime in <audio> tag

Struggling to create a progress bar for my HTML5 audioplayer and implement a function to change the playing track's time by tapping. I opted to use input[range], but encountering an issue where the current play time does not update when tapping on the ...

Tips for acquiring an object to utilize in v-bind within a v-for loop

Consider this code snippet: <ol class="breadcrumb arr-right"> <li v-for="(url,name, index) in links" v-bind:class=" (index == (links.length -1)) ? 'breadcrumb-item active' : 'breadcrumb-item'"> <a v-bind:href ...

Setting up the Webpack output bundle configuration

Recently delving into React development with Webpack, I set up a boilerplate by following an insightful tutorial article. While grasping the fundamentals of webpack and able to follow the tutorial easily, I am facing difficulty in comprehending how my spe ...

Show a webpage depending on specific JavaScript criteria

I have a condition in my JavaScript code that determines whether or not a user should be granted access to a specific page. However, I don't want users to be able to directly access this page even if they know the URL. This page contains both HTML and ...

What is the best way to find the maximum and minimum values within a JSON object that is populated from user inputs using only JavaScript?

Here is a simple form that stores data at the following link: https://jsfiddle.net/andresmcio/vLp84acv/ var _newStudent = { "code": code, "names": names, "grade": grades, }; I'm struggling to retrieve the highest and lowe ...

The dimensions of GridStack items specified in pixels for both height and width

I am facing a challenge with my GridStack items, which each contain elements like graphs that need to be re-rendered when the size of the gridstack item (cell) changes. I am attempting to use the change event on GridStack to detect the modified items and t ...

Converting a one-dimensional array into a two-dimensional array in JavaScript explained

Below is the code snippet const arrayColumn = (arr, n) => arr.map(x => x[n]); const pcorr = (x, y) => { let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0; const minLength = x.length = y.length = Math.min(x.length, y.le ...

The RouterView component seems to be malfunctioning within the Vue project

I recently started working on a vue-project where I created a Home page with a sideBar component and a routerView component. Here's a preview: https://i.sstatic.net/f51tdsZ6.png When clicking on any sidebar item, the router navigates to the corresp ...

Transferring form data to a PHP script with the help of JavaScript/jQuery

Struggling to figure out how to pass form values to a PHP script using JS. The PHP script is functioning correctly by saving the data into a text file with additional information such as IP and Date/Time. Despite being a simple issue, my lack of JS knowl ...

What is the process for generating a watermark directive in angularjs?

My application utilizes a jQuery script for watermarking textboxes. I created a directive to easily apply this watermark to input elements, but it's not functioning as expected. While debugging, I can see the watermark being added, but once the UI fin ...

Clicking on the modal button causes the code to run multiple times in JQuery and JavaScript

Hello, I'm experiencing an issue where the code inside a modal is being executed multiple times when the modal button is clicked. For example, if I click the modal button once, the code runs once; if I click it twice, the code runs twice, and so on. ...

Determining the file path relative to the project/src directory in Node.js

Currently, in my node.js project I am utilizing log4js and aiming to include the file name where the log record was added. However, when using __filename, it provides me with the absolute path. var logger = log4js.getLogger(__filename) This results in lo ...