Simple Calculator using Vue and Bootstrap 5
Carol Skelly
Posted on June 23, 2021
The HTML Markup
Bootstrap 5 provides all the UI styling and responsive behavior. It's a great fit with Vue now that it's no longer dependent on jQuery. The Bootstrap grid row
& col-*
are used to responsively change the calculator width as screen size changes.
<div class="row">
<div class="col-xxl-2 col-lg-3 col-md-4 col-sm-6 mx-auto bg-dark rounded-3 shadow-sm p-3">
<input class="form-control form-control-lg text-success" v-model="calculator.displayValue" />
<!-- calculator number pad using grid -->
<div class="row g-0 text-center mt-2">
<div class="col-auto text-white">
<div class="row g-1 g-lg-1">
<div v-for="(key,i) in keypad" :key="i" class="ms-auto col-3 py-2">
<button class="btn btn-dark text-warning w-100" @click="processKey(key.value)">{{ key.label }}</button>
</div>
<div class="col-12 pt-2">
<button class="btn btn-dark border-secondary btn-lg text-warning w-100 fw-bold lead" @click="processKey('=')">=</button>
</div>
<div class="col-12">
<div v-if="errValue" class="alert alert-warning p-2 text-truncate small" role="alert">
{{ errValue }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
The Vue App Data
Calculator state is kept in it's own data calculator
object to track the displayValue
, firstOperand
and operator
. waitingForSecondOperand
let's us know when it's ready to perform a calculation.
The keypad
array is ordered by how I wanted the keys to render as columns inside the Bootstrap row
...
<div v-for="(key,i) in keypad" :key="i" class="ms-auto col-3 py-2">
<button class="btn btn-dark w-100" @click="processKey(key.value)">{{ key.label }}</button>
</div>
...
data () {
return {
calculator: {
displayValue: '0',
firstOperand: null,
waitingForSecondOperand: false,
operator: null,
},
errValue: null,
keypad: [
{label:'7', value: 7},
{label:'8', value: 8},
{label:'9', value: 9},
{label:'x', value: '*'},
{label:'4', value: 4},
{label:'5', value: 5},
{label:'6', value: 6},
{label:'+', value: '+'},
{label:'1', value: 1},
{label:'2', value: 2},
{label:'3', value: 3},
{label:'-', value: '-'},
{label:'AC', value: 'AC'},
{label:'.', value: '.'},
{label:'0', value: 0},
{label:'/', value: '/'},
],
},
...
The Vue App Methods
Of course the calculator actually needs to calculate so I define a series of methods to watch the keystrokes and perform basic math functions. Most of them should be self-explanatory. You will see that errValue
is used to store any errors, and resets upon keypress or clear.
When a key is pressed, processKey()
is called to determine which key and then call the appropriate function...
-
inputDigit()
- when any digit is clicked -
handleOperator()
- when any operator is clicked -
equalPressed()
- when the equal sign is clicked -
inputDecimal()
- to handle the decimal point (.) -
resetCalculator()
- to clear the calculator ('AC')
...
methods: {
processKey: function(val) {
this.errValue = null
switch (val){
case "AC": this.resetCalculator()
break;
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9: this.inputDigit(val)
break;
case "+": this.handleOperator("+")
break;
case "-": this.handleOperator("-")
break;
case "/": this.handleOperator("/")
break;
case "*": this.handleOperator("*")
break;
case "=": this.equalPressed();
break;
case ".": this.inputDecimal(".")
break;
default:
this.errValue = 'KEY ERROR: in default'
}
},
equalPressed() {
const { firstOperand, displayValue, operator } = this.calculator
try{
this.calculator.displayValue = this.calculate(firstOperand, displayValue, operator)
}
catch (e){
this.errValue = e
}
},
inputDigit(digit) {
const { displayValue, waitingForSecondOperand } = this.calculator
console.log(waitingForSecondOperand)
if (waitingForSecondOperand === true) {
this.calculator.displayValue = digit
this.calculator.waitingForSecondOperand = false
} else {
console.log(displayValue)
this.calculator.displayValue =
displayValue === '0' ? digit : displayValue + '' + digit
}
},
inputDecimal(dot) {
const { displayValue, waitingForSecondOperand } = this.calculator
if (waitingForSecondOperand === true) {
this.calculator.displayValue = '0.'
this.calculator.waitingForSecondOperand = false
return
}
// check for existing decimal
if (displayValue % 1 === 0) {
this.calculator.displayValue += dot
}
},
handleOperator(nextOperator) {
const { firstOperand, displayValue, operator, waitingForSecondOperand } = this.calculator
const inputValue = parseFloat(displayValue)
if (operator && waitingForSecondOperand) {
this.calculator.operator = nextOperator
return
}
if (firstOperand == null && !isNaN(inputValue)) {
this.calculator.firstOperand = inputValue
} else if (operator) {
const currentValue = firstOperand || 0
const result = this.calculate(currentValue, inputValue, operator)
this.calculator.displayValue = String(result)
this.calculator.firstOperand = result
}
this.calculator.waitingForSecondOperand = true
this.calculator.operator = nextOperator
},
calculate(firstOperand, secondOperand, operator) {
if (operator === '+') {
return firstOperand + secondOperand
} else if (operator === '-') {
return firstOperand - secondOperand
} else if (operator === '*') {
return firstOperand * secondOperand
} else if (operator === '/') {
if (secondOperand == 0){
this.errValue = 'ERROR: Cannot divide by 0'
}
else {
return firstOperand / secondOperand
}
}
return secondOperand
},
resetCalculator() {
this.calculator.displayValue = '0'
this.calculator.firstOperand = null
this.calculator.waitingForSecondOperand = false
this.calculator.operator = null
},
},
...
As always, you can play with the latest Bootstrap 5, and find more handy Vue snippets and examples on Codeply!
Thanks for reading!
Posted on June 23, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.