I have been struggling to use Vue and MathLive to handle typesetting randomly generated numbers and their squares. The function of the program is to generate a random integer from 1 to 35, calculate the square, and typeset it with MathLive. There are two buttons that add one to the integer or create another random one. I have no problem typesetting the initial value but when I create a different integer or add 1 the page, it never re-typesets. I am trying to implement this program as a component in Vue. Here is my MWE (component only):
<template lang="html">
<div class="problem">
<p id="math">$${{num}}^2 = {{square()}}$$</p>
<button @click="addOne">Add One</button>
<button @click="randomInt">Random Number</button>
</div>
</template>
<script>
import math from 'mathjs'
import MathLive from 'mathlive'
export default {
name: 'Problem',
data: function () {
return {
num: math.randomInt(1,35)
}
},
watch: {
num: function () {
console.log("Data changed");
// this.renderMath();
}
},
created: function () {
console.log("Hello This is created!");
this.renderMath();
},
beforeMount: function () {
console.log("This is beforeMount");
},
mounted: function () {
console.log("This is mounted!");
},
beforeUpdate: function () {
console.log("This is beforeUpdate");
this.renderMath();
},
methods: {
addOne: function() {
this.num++
},
randomInt: function () {
this.num = math.randomInt(1,35)
},
square: function () {
return this.num**2
},
renderMath: function (event) {
this.$nextTick(function(){
MathLive.renderMathInElement("math");
})
}
}
}
</script>
<style lang="css" scoped>
@import url("../../node_modules/mathlive/dist/mathlive.core.css");
@import url("../../node_modules/mathlive/dist/mathlive.css");
p {
color: white;
}
</style>
Edit: To clarify when I load the page up, the initial value is typeset correctly using MathLive as shown below: Then after I click either the Add One or Random Number button, the program should generate a new value, calculate its square, and update that value on the screen as shown below:
It seems MathLive's DOM manipulation conflicts with Vue's virtual DOM, preventing Vue from patching the DOM with the updated text node.
A workaround is to apply a key
to force the MathLive p
element to be re-created when the key
changes. We could use num
as the key, since it changes with each button press:
<p :key="num">...</p>
The current watcher on num
would need to be updated to call renderMath()
to refresh the MathLive element:
watch: {
num() {
this.renderMath();
}
},
You should also consider making square()
a computed property for more efficient rendering:
// script
computed: {
square() {
return this.num ** 2
}
}
// template
<p :key="num">$${{num}}^2 = {{square}}$$</p>