How to fix Vue 3 template compilation error : v-model value must be a valid JavaScript member expression? - vue.js

I am working on a vue project and the vue version is 3.0
And recently I can see these many warnings for some reason.
Template compilation error: v-model value must be a valid JavaScript member expression
I guess it is because I am using long v-model variable name like this.
<textarea v-model="firstVariable.subVariable.subVariableKey" readonly></textarea>
Please let me know if any idea.
Thanks in advance
This is the component and template code.
var myTemplate = Vue.defineComponent({
template: '#myTemplate',
data() {
return {
firstVariable: {}
}
},
mounted() {
loadData();
},
methods:{
loadData() {
axios.get(MY_ROUTES).then(res => {
// let's suppose res.data is going to be {subVariable: {subVariableKey: "val"}}
this.firstVariable = res.data;
})
}
}
});
// template.html
<script type="text/template" id="myTemplate">
<div class="container">
<textarea v-model="firstVariable.subVariable?.subVariableKey"></textarea>
</div>
</script>

In order that your property go reactive you've to define its full schema :
data() {
return {
firstVariable: {
subVariable: {
subVariableKey: ''
}
}
}
},
and use it directly without optional chaining
v-model="firstVariable.subVariable.subVariableKey"
because v-model="firstVariable.subVariable?.subVariableKey" malformed expression like v-model="a+b" like this test
Example
var comp1 = Vue.defineComponent({
name: 'comp1',
template: '#myTemplate',
data() {
return {
firstVariable: {
subVariable: {
subVariableKey: ''
}
}
}
},
mounted() {
this.loadData();
},
methods: {
loadData() {
}
}
});
const {
createApp
} = Vue;
const App = {
components: {
comp1
},
data() {
return {
}
},
mounted() {
}
}
const app = createApp(App)
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" >
vue 3 app
<comp1 />
</div>
<script type="text/template" id="myTemplate">
<div class="container">
<textarea v-model="firstVariable.subVariable.subVariableKey"></textarea>
<div>
{{firstVariable.subVariable.subVariableKey}}
</div>
</div>
</script>

You are adding a new property to an object which is not reactive.
Vue cannot detect property addition or deletion. Since Vue performs
the getter/setter conversion process during instance initialization, a
property must be present in the data object in order for Vue to
convert it and make it reactive. For example:
Source
Instead of
this.firstVariable = res.data;
Use
this.$set(this.firstVariable, 'subVariable', res.data.subVariable);

Related

Vue pass data from compontent 1 to a method in component 2

I have a parent component and a childcomponent.
In my parent component I call a simple childcomponent-method to save an email to the email variable. But the variable email does not change.
My Parentcomponent:
import ChildComponent from "./ChildComponent";
export default {
components: {ChildComponent},
methods: {
openDocument(d) {
ChildComponent.methods.saveEmail('new#example.com');
}
}
My Childcomponent:
<template>
<div>
Email: {{ email }}
</div>
</template>
<script>
export default {
data: function () {
return {
email: ''
}
},
methods: {
saveEmail(email) {
this.email = email; // this does NOT change my email variable
}
}
}
</script>
Why my email variable does not change? How can I change this variable?
In vue it is not work like that. You have to use Probs:
Parent :
<template>
<div class="container">
<child-component :email ="email"></child-component> // NEW HERE
</div>
</template>
<script>
import ChildComponent from "./ChildComponent";
module.exports = {
data: function () {
return {
email:''
}
},
methods: {
openDocument(d) {
this.email = "example#gmil.com"
}
},
}
</script>
Child component:
<template>
<div class="container">
<h1>Profile Form Component</h1>
</div>
</template>
<script>
module.exports = {
module.exports = {
props: ['email'], //NEW HERE
created: function () {
console.log(this.email) //prints out an empty string
}
}
</script>
ATTENTION
As you I added 2 comment NEW HERE in the code , these 2 lines are really important for what you wanna do.
The code that I giving you is an example (not a complete answer) , Probs is the solution of what you asked for.
Hope it Helps <3.
The ChildComponent variable only holds the recipe for creating components of this type - but it does not hold your actual component. Your actual component lives inside your template - you have to add a ref attribute to it (e.g. <custom-component ref="custom" ... />) and then reference it like this.$refs.custom.saveEmail()

How do I make reactive global property in vuejs plugin?

I put $testCounter in a plugin to make it global :
Vue.use({
install(Vue) {
Vue.prototype.$testCounter = 0;
Vue.prototype.$incrementCounter = () => {
Vue.prototype.$testCounter++;
};
});
I want to output it in some component. I also need its value to be updated globally, and reactively :
<template>
<p>{{ $testCounter }}</p>
</template>
<script>
mounted() {
let comp = this;
comp.watcherId = setInterval(() => {
comp.$incrementCounter();
// I want to remove this line and still be reactive :
comp.$forceUpdate();
}, 1000);
}
</script>
I need the property to be reactive, I tried a multiple solution as watchers, computed props, vm.$set(...), but I can't find the right way to do this.
Solution 1: use Vuex
Solution 2: set the global reactive data in root component and use it by calling this.$root
demo: https://jsfiddle.net/jacobgoh101/mdt4673d/2/
HTML
<div id="app">
<test1>
{{$root.testCounter}}
</test1>
</div>
Javascript
Vue.component('test1', {
template: `
<div>
test1
<slot></slot>
</div>
`,
mounted() {
setInterval(() => {
this.$root.incrementCounter();
}, 1000)
}
});
new Vue({
el: "#app",
data: {
testCounter: 1
},
methods: {
incrementCounter: function() {
this.testCounter++;
}
}
})

Vue model not updating

When I try to update my custom text-area component's model data this.message='<span id="foo">bar</span> the text and html does not display in the htmltextarea tag like it should, but I can see the update applied in the Vue dev tool's console. I've also tried switching to an object instead of a string and using Vue.set, but this does not work either.
Any suggestions on how to fix this?
The goal with the htmlTextArea component is to get the users text from the htmlTextArea tag (this works), manipulate this text and bind it back to the textarea, but with HTML in it.
Custom text-area component:
<template>
<div contenteditable="true" #input="updateHTML" class="textareaRoot"></div>
</template>
<script>
export default {
// Custom textarea
name: 'htmlTextArea',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
}
</script>
Other component:
<template>
...
<htmlTextArea id="textarea" v-model="message"></htmlTextArea>
...
</template>
<script>
data: {
return {
message: 'something'//this works
}
}
...
methods: {
changeText() {
this.message='<span id="foo">bar</span>'//this does not
}
},
components: {
htmlTextArea
}
</script>
You need to set the value explicitly after the value props change. you can watch for value change.
<template>
<div contenteditable="true" #input="updateHTML" class="textareaRoot"></div>
</template>
<script>
export default {
// Custom textarea
name: "htmlTextArea",
props: ["value"],
mounted: function() {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
this.$el.innerHTML = v;
}
},
methods: {
updateHTML: function(e) {
this.$emit("input", e.target.innerHTML);
}
}
};
</script>
Change the data property into a function, as you have it defined it is not reactive.
data () {
return {
message: 'something'//this works
}
}
Now when you update the message property in your method, the component will update accordingly.
Reactivity in depth

Is that the right way to set an id in Vue.js component?

I am trying to integrate Phaser 3 with Vue.js 2.
My goal is to create a Vue.js component associated with a game canvas.
My initial solution was:
<template>
<div :id="id">
</div>
</template>
<script>
import Phaser from 'phaser'
export default {
data () {
return {
id: null,
game: null
}
},
mounted () {
this.id = 'game' + this._uid
var config = {
parent: this.id,
type: Phaser.CANVAS
}
this.game = new Phaser.Game(config)
....
}
}
</script>
This code attach the game canvas to my template. But to my surprise it only worked 'sometimes'.
I figured out, after hours of debugging, that my div element in the DOM wasn't updated with the id when I was instantiating my new Game.
So I came up with the solution of instantiating the id in the beforeMount () method as follow:
<template>
<div :id="id">
</div>
</template>
<script>
import Phaser from 'phaser'
export default {
data () {
return {
id: null,
game: null
}
},
beforeMount () {
this.id = 'game' + this._uid
},
mounted () {
var config = {
parent: this.id,
type: Phaser.CANVAS
}
this.game = new Phaser.Game(config)
....
}
}
</script>
It is working, but I would like to know if there is a more simple and elegant solution ?
One better solution for integrating Phaser.Game into the application is directly passing the config the HTML element, a configuration supported by Phaser.Game.
To get a reference to a HTML element in vue, you can use refs, these are basically id's, but local to the component itself, so there is not risk in creating conflicts.
<template>
<div ref="myDiv">
</div>
</template>
<script>
import Phaser from 'phaser'
export default {
data () {
return {
game: null
}
},
mounted () {
var config = {
parent: this.$refs.myDiv,
type: Phaser.CANVAS
}
this.game = new Phaser.Game(config)
....
}
}
</script>
Vue3 sample:
<script setup>
import { ref,onMounted } from 'vue';
import Phaser from 'phaser'
const myDiv = ref(null)
let canvasWidth = 750;
let canvasHeight = 1450;
onMounted(() => {
const config = {
type: Phaser.AUTO,
parent: popWrap.value,
width: canvasWidth,
height: canvasHeight,
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
})
</script>
<template>
<div ref="myDiv">
</div>
</template>

VueJs how to get data from Vue.component

I want to know how to get data from Vue.component and send to
this >>var app = new Vue({ })<<
this is my code
Vue.component('my-form', {
template: '#my-form',
props:['ddTechtemp'],
data: function () {
return {
isCores: app.testCorres,
activeClass: 'isCorrespon',
errorClass: 'isTech',
tempData : {cell1 : "",
cell2 : "",
cell3 : "",
cell4 : "",
cell5 : "",
cell6 : ""
},
}
},
watch:{
tempData:{
handler:function(newVal,oldVal){
app.ddTechtemp = newVal;
},
deep:true,
}
},
methods:{
}});
I want to get data from above code and send to this code var app = new Vue({ data: Vue.component.data})
Anyone understand me please help.Thank you
In Vue.js parent-child relationship is run by
1) passing props from parent to child
2) emitting custom events from child to parent
So, if you need to pass some data from child to parent - use this.$emit to emit a custom event with your data, and listen for this event in parent component with v-on:myevent or #myevent shorthand. The data you pass with event is found in $event variable.
Example: https://jsfiddle.net/wostex/63t082p2/51/
<div id="app">
<myform #newdata="handleData($event)"></myform>
<p>name: {{ user.name }}</p>
<p>age: {{ user.age }}</p>
</div>
new Vue({
el: '#app',
data: {
user: { name: '', age: 0 }
},
methods: {
handleData: function(e) {
[this.user.name, this.user.age] = e;
}
},
components: {
'myform': {
template: `
<div>
<input type="text" v-model="formData.name" placeholder="Your name">
<input type="number" v-model.number="formData.age">
</div>`,
data: function() {
return { formData: { name: '', age: 0 } }
},
watch: {
formData: {
handler: function() {
this.$emit('newdata', [this.formData.name, this.formData.age]);
},
deep: true
}
}
}
}
});
Another way would be to work with advanced things like a Vuex store for ''state management'' but for simple implementations one additional reactive component would work as well.
in src/store/index.js
import { reactive } from 'vue';
export const store = reactive({
some_id : 0,
});
And in a component src/component/SelectComponent.vue
<script setup>
import { store } from "#/store";
</script>
<script>
export default {
name: "SelectComponent",
// rest of the component source here
}
</script>
<template>
<select v-model="store.some_id">
<option v-for="itm in list" :key=itm.id" :value="itm.id">{{ itm.text }}</option>
</select>
</template>
Using the component in src/views/SomeView.vue
<script setup>
import { store } from "#/store";
import SelectComponent from "#/components/SelectComponent"
</script>
<script>
//... use store.some_id in some method
</script>
You can store all your global variables in the store/index.js file and keep reactive. You might want to add watchers to observe the store variable(s).
WARNING: this is discouraged for large, complex Vue applications
NOTE: this is an easy approach for simple state management not requrring Vuex with all the actions and mutations, states and contexts - plumbing.