I’m working in a Nuxt JS 2 project and have built a component called Calculator. This is loaded into my page which contains a HTML table. I have a button in the page that toggles the calculator, the variable to toggle is a boolean isCalculatorShown.
When my calculator component is initially toggled on everything works, but when the isCalculatorShown variable then gets switched from true back to false (essentially destroying the component) and then toggled on again, everything works apart from the output to the page of my formulas object.
Console logging in the calculateCells method shows me the correct values, yet trying to do this.formulas.* doesn’t update the one inside data(), what am I missing, here’s my Calculator component:
<template>
<div class="calculator-wrapper">
<b-button @click="closeCalculator" pill variant="white" size="sm" class="btn-close shadow">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" width="22" stroke-width="1.5" stroke="currentColor" class="text-secondary">
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</b-button>
<div class="sums p-1">
<article>
<div class="d-none d-md-flex">
<ul class="list-group list-group-horizontal bg-transparent">
<li class="list-group-item">Sum: {{ formulas.sum }}</li>
<li class="list-group-item">Avg: {{ formulas.avg }}</li>
<li class="list-group-item">Min: {{ formulas.min }}</li>
<li class="list-group-item">Max: {{ formulas.max }}</li>
<li class="list-group-item">Count: {{ formulas.count }}</li>
</ul>
</div>
<div class="d-flex d-md-none">
<select name="cell-calculations" class="custom-select">
<option>Sum: {{ formulas.sum }}</option>
<option>Avg: {{ formulas.avg }}</option>
<option>Min: {{ formulas.min }}</option>
<option>Max: {{ formulas.max }}</option>
<option>Count: {{ formulas.count }}</option>
</select>
</div>
</article>
<div class="p-1">
<b-button @click="clear" variant="dark" size="sm">Clear</b-button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
isCalculatorShown: {
default: false,
type: Boolean
},
dataScope: {
default: 'td',
type: String
}
},
data () {
return {
calculator: null,
table: null,
cells: null,
cellValues: [],
formulas: {
sum: 0,
avg: 0,
min: 0,
max: 0,
count: 0
}
}
},
mounted () {
this.initCalculator()
for (const cell of this.cells) {
cell.addEventListener('click', (event) => {
this.getCellValue(cell)
})
}
},
methods: {
/*
** Init calculator
*/
initCalculator () {
this.calculator = document.querySelector('.calculator-wrapper')
this.table = document.getElementById('main-report')
if (!this.table) return
this.cells = this.table.querySelectorAll(this.dataScope)
},
/*
** Calculate value of selected table cells
*/
calculateCells (val) {
// TODO: this.formulas never seems to update one in data() after component v-if toggled
this.formulas.sum = val.map(Number).reduce((a, b) => a + b, 0).toFixed(2)
this.formulas.avg = (val.reduce((a, b) => a + b, 0) / val.length).toFixed(2)
this.formulas.min = Math.min(...val)
this.formulas.max = Math.max(...val) || 0
this.formulas.count = val.length || 0
},
getCellValue (cell) {
const SANITISED_REGEX = /^£|,|%|s|([^)]*) */g
const trimmedCellValue = cell.innerText.trim().replace(/^-£|£-/g, '-')
const firstCellValue = this.getFirstCellValue(trimmedCellValue)
if (firstCellValue) {
const sanitisedValue = firstCellValue.replace(SANITISED_REGEX, '')
if (
this.isNumeric(sanitisedValue) &&
!cell.classList.contains('highlight-cell')
) {
this.cellValues.push(sanitisedValue)
cell.classList.add('highlight-cell')
this.calculateCells(this.cellValues.map(Number))
}
} else {
if (
this.isNumeric(trimmedCellValue.replace(SANITISED_REGEX, '')) &&
!cell.classList.contains('highlight-cell')
) {
cell.classList.add('highlight-cell')
this.cellValues.push(trimmedCellValue.replace(SANITISED_REGEX, ''))
this.calculateCells(this.cellValues.map(Number))
}
}
},
/*
** Check if selected cell is a number
*/
isNumeric (str) {
if (typeof str != 'string') return false
return !isNaN(str) && !isNaN(parseFloat(str))
},
/*
** Return first value if there are multiple within the table cell
*/
getFirstCellValue (cell) {
return cell.split(' ')[0]
},
/*
** Close calculator
*/
closeCalculator () {
this.$emit('onClose', true)
this.clear()
},
/*
** Clear any selected values
*/
clear () {
this.cellValues = []
this.formulas.sum = 0
this.formulas.avg = 0
this.formulas.min = 0
this.formulas.max = 0
this.formulas.count = 0
for (const cell of this.cells) {
cell.classList.remove('highlight-cell')
cell.removeEventListener('click', this.getCellValue)
}
}
}
}
</script>
And it’s loaded at the page level with:
<Calculator v-if="isCalculatorShown" @onClose="isCalculatorShown = false" />