I have a Class
with a proxy-based object, the set()
method changes another property of the same class, everything works fine if I run the code only in JS/TS.
class Form {
errors = []
values = {}
constructor(values) {
this.values = new Proxy(values, {
set: (target, prop, value) => {
target[prop] = value
this.errors.push('test')
return true
},
})
}
}
const form = new Form({
name: 'Jhon',
age: 20,
})
form.values.name = 'Maria'
form.values.age = 30
console.log(form.errors)
Expected result of form.errors
is an Array like ['test', 'test']
But if I run it inside Vue, using {{ form.errors }}
inside <template>
it's not reactive, it's not updated in real time.
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<script src="https://unpkg.com/vue@3"></script>
<div id="app">{{ form.errors }}</div>
<script>
class Form {
errors = []
values = {}
constructor(values) {
this.values = new Proxy(values, {
set: (target, prop, value) => {
target[prop] = value
this.errors.push('test')
return true
},
})
}
}
const app = Vue.createApp({
data() {
return {
form: new Form({
name: 'Jhon',
age: 20,
}),
}
},
mounted() {
this.form.values.name = 'Maria'
this.form.values.age = 30
},
})
app.mount('#app')
</script>
</body>
</html>
form.errors
is updated, but this does not reflect in Vue, it is as if Vue cannot observe these changes, the proof of this is that if we do
mounted() {
this.form.values.name = 'Maria'
this.form.values.age = 30
this.form.errors.push('hello')
}
we will have the expected result in the DOM
['test', 'test', 'hello']
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<script src="https://unpkg.com/vue@3"></script>
<div id="app">{{ form.errors }}</div>
<script>
class Form {
errors = []
values = {}
constructor(values) {
this.values = new Proxy(values, {
set: (target, prop, value) => {
target[prop] = value
this.errors.push('test')
return true
},
})
}
}
const app = Vue.createApp({
data() {
return {
form: new Form({
name: 'Jhon',
age: 20,
}),
}
},
mounted() {
this.form.values.name = 'Maria'
this.form.values.age = 30
this.form.errors.push('okay')
},
})
app.mount('#app')
</script>
</body>
</html>
Could anyone help me solve this problem or make this work?
What I want is for form.errors
to be reactive in Vue just like any other property.
CodePudding user response:
To make Form#errors
reactive in this case, initialize it with Vue.reactive()
:
class Form {
errors = Vue.reactive([])
⋮
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app">form.errors: {{ form.errors }}</div>
<script>
class Form {
errors = Vue.reactive([])
values = {}
constructor(values) {
this.values = new Proxy(values, {
set: (target, prop, value) => {
target[prop] = value
this.errors.push('test')
return true
},
})
}
}
const app = Vue.createApp({
data() {
return {
form: new Form({
name: 'Jhon',
age: 20,
}),
}
},
mounted() {
this.form.values.name = 'Maria'
this.form.values.age = 30
this.form.errors.push('hello')
},
})
app.mount('#app')
</script>
</body>
</html>