Reputation: 1025
I know that a similar question has already been dealt with on stackoverflow. But I could not put together a solution from the proposed one. I am very ashamed.
The essence is this: I have a component and another one inside it. The child component-VClipboardTextField is a ready-made configured text-area. I couldn't get the input out of there and I don't get an error when I try to enter it.
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "message"
<template>
<v-container fluid>
<v-row align="center">
<v-col cols="9">
<v-card flat>
<v-card-text>
<h1>Request</h1>
<v-container>
<v-textarea v-model="message"
placeholder="Placeholder"
label="Request"
auto-grow
clear-icon="mdi-close-circle-outline"
clearable
rows="10"
row-height="5"
@click:clear="clearMessage"
></v-textarea>
<v-textarea v-model="response"
placeholder="Placeholder"
label="Request2"
auto-grow
counter
rows="10"
row-height="5"
color="success"
></v-textarea>
<VClipboardTextField ></VClipboardTextField>
<VClipboardTextField isReadOnly></VClipboardTextField>
</v-container>
<v-row>
<v-btn
dark
color="primary"
elevation="12"
justify="end"
float-right
@click="sendRequest"
>
Send Request
</v-btn>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="3">
<schema-selector @changeSchema="onChangeSchema"></schema-selector>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'tabs-item',
props: ['response'],
data() {
return {
schema: String,
message: '',
}
},
methods: {
sendRequest() {
const message = {
message: this.message,
method: this.schema.state
}
this.$emit('sendRequest', message)
},
clearMessage() {
this.message = ''
},
onChangeSchema(selectS) {
console.log("get schema: ", selectS.state)
this.schema = selectS
}
},
}
</script>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
@click:append-outer="copyToBuffer"
@click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
}
},
methods: {
copyToBuffer() {
console.log("this: ", this)
navigator.clipboard.writeText(this.message);
this.toolTipChange()
setTimeout(() => this.toolTipChange(), 1000)
},
clearMessage() {
this.message = ''
},
toolTipChange() {
if (this.show)
this.show = false
}
}
}
</script>
I will be glad to see an example of the code that will explain how to correctly solve this problem without crutches! Thanks.
Upvotes: 0
Views: 457
Reputation: 1033
Instead of watching for change every time, you can only emit when there are changes.
In your tabs-item.vue
<VClipboardTextField v-model="message"></VClipboardTextField>
In your VClipboardTextField.vue component, you can receive the v-model input prop as a "VALUE" prop and assign it to a local data property. That way u can manipulate the local property and emit only when in it is changed!
<template>
<v-container>
<v-tooltip bottom v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
v-on="on"
v-bind="attrs"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
@click:append-outer="copyToBuffer"
@click:clear="clearMessage"
@change="$emit('input', v)"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: "VClipboardTextField",
props: {
isReadOnly: { type: Boolean },
value: {
type: String,
},
},
data() {
return {
show: false,
message: this.value,
};
},
};
</script>
Upvotes: 1
Reputation: 743
you cannot modify props in components, if the initial value of message is needed as placeholder (meaning the input might not be empty at the begining), you can store the data in message
prop to another data variable and use that as v-model
to the textarea
.
if there is no initial value for message, just use another data variable for textarea and emit an update for message in a watcher.
in code tabs-item.vue
<VClipboardTextField :message.sync="message"></VClipboardTextField>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message_slug"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
@click:append-outer="copyToBuffer"
@click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
message_slug: '',
}
},
watch: {
message_slug(x) {
this.$emit('update:message', x)
}
},
}
</script>
It will bind the value to message_slug
and update the message
on parent component when it's value changes.
Upvotes: 1