Vuex mutation updates state, computed property never reflects update in markup - javascript

This issue hasn't been touched in a while but i'm running into something confusing on my end when using a computed property for a textarea value.
I have a textarea where you give input text and on input it updates the input text in vuex:
<textarea
ref="inputText"
:value="getInputText"
#input="setInputText"
class="textarea"
placeholder="Your message goes in here"
></textarea>
On the click of a button to translate the text I call a handleInput method.
handleInput() {
this.$store.dispatch("translateEnglishToRussian");
},
In my store I have my translateEnglishToRussian action:
translateEnglishToRussian({ commit }) {
const TRANSLATE_API = "https://XXXXXXXX.us-east-1.amazonaws.com/prod/YYYY/";
const data = JSON.stringify({ english: this.state.inputText });
this.$axios
.$post(TRANSLATE_API, data)
.then(data => {
commit("setOutputText", data.translatedText);
commit("setMP3Track", data.mp3Path);
})
.catch(err => {
console.log(err);
});
}
I see it call setOutputText mutation with the payload of translated text, in my vue dev tools I see the correct state with the translated text. However, my second text area that's being used purely to display the translated text never updates!
Output textarea:
<textarea
disabled
ref="outputText"
:value="getOutputText"
class="textarea"
></textarea>
Its value is bound to a computed property called getOutputText:
getOutputText() {
return this.$store.state.outputText;
}
What am i doing wrong here! Any advice is appreciated. I thought this should be fine since i'm using commit in my vuex action in a synchronous way (inside the then() block).
Edit: I have the same result if I also try using v-model. The initial output text from vuex state is rendered there on page load. When I translate, I see the update in Vue Dev Tools correctly, but the text in the text area never re-renders.
Edit #2: Here is my setOutputText mutation:
setOutputText(state, payload) {
console.log(`state - set output - ${payload}`);
state.outputText = payload;
},

After looking at the vue docs for Multiline text, you should replace :value="getOutputText" with v-model="getOutputText".
Because it's computed property, to use it in v-model you need to add get and set to your computed property
<textarea
disabled
ref="outputText"
v-model="getOutputText"
class="textarea"
></textarea>
EDIT: Per #Stephen Tetreault comment, v-model didn't work for him, but :value did solve the problem at the end.
computed: {
getOutputText: {
// getter
get: function () {
return this.$store.state.outputText;
},
// setter
set: function (newValue) {
// there is no need to set anything here
}
}
}

Related

How to modify Vue.js input element value from console

Given the following code below where <v-input> is a custom input element,
<template>
<v-input id="username" type="text" v-model="username" />
</template>
<script>
export default {
data() {
return {
username: "",
};
},
};
</script>
How would one go about modifying the value of the input element through the browser console? I have tried the following code below but it does not bind to the Vue component.
const element = document.querySelector("#username");
element.value = "foobar"
I've also tried emitting custom events but those don't seem to work for me either. Any advice regarding this would be much appreciated.
I figured it out, I need only dispatch a new input event so that Vue.js catches the value. See below for an example.
const inputBox = document.querySelector("#inputBox");
inputBox.value = "hello";
inputBox.dispatchEvent(new Event('input'));

Trying to emit result from bus vuejs

I am trying to emit result number of quotes where I add plus one and also push result of the text from textarea to array. The string text is pushed to array successfully but the number of quotes is always zero. I have tried it in one method or separed and I always get same result.
<template>
<div>
<p>Quotes</p>
<b-form-textarea
id="textarea-rows"
v-model="value"
placeholder="Enter something..."
rows="5"
></b-form-textarea>
<b-button variant="primary" #click='plusOneQuote(); addQuote();'>Add Quote</b-button>
</div>
</template>
<script>
import {EventBus} from '../main.js'
export default {
props: ['numberOfQuotes', 'quotes'],
data (){
return {
value: '',
}
},
methods: {
plusOneQuote() {
this.numberOfQuotes++;
EventBus.$emit('addedQuote', this.numberOfQuotes);
},
addQuote() {
this.quotes.push(this.value);
}
}
}
</script>
When I delete the addQuote from click and the method, then numberOfQuotes get bigger by one and I show that in another component by no problem.
Is a good practice to call a single method on the click directive, so instead of:
<b-button variant="primary" #click='plusOneQuote(); addQuote();'>Add Quote</b-button>here
you could implement
<b-button variant="primary" #click='updateQuotes'>Add Quote</b-button> here
And the methods section
methods: {
plusOneQuote() {
this.numberOfQuotes++;
EventBus.$emit('addedQuote', this.numberOfQuotes);
},
addQuote() {
this.quotes.push(this.value);
},
updateQuotes(){
this.plusOneQuote();
this.addQuote();
}
I had this problem before and hope this solution helps.
EDIT:
Sorry, missed some context, since you are trying push to this.quotes which is a prop, you cannot directly mutate it, you should use a getter and a setter to do it or even better, use a v-model. Quoting the docs:
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: "visible" (found in component )

Change component value

I am currently building a weather web app with React, and now I have a question: How do I change my components (SaveWeather) city-value?
Say that you clicked on a button where you entered 'Berlin', and the code below runs. The this.state.value is 'Berlin', and I want to add SaveWeather component with this value. But if I enter my next value, say 'London', The this.state.value is still 'Berlin'. So I want to update the city-value of SaveWeather every time the user enters new information.
test() {
this.setState({
saveWeather: [this.state.saveWeather, <SaveWeather city={this.state.value} key="1" />]
});
}
Thanks in advance!
You shouldn't really save components in your state, only data. The way I'd do this would be something like this.
Your state:
{value: "Initial Value that can be an empty string"}
A function that handles the value changes:
handleValueChange(e) {
// e is the event that comes from the input element (where the user writes the city they want)
const city = e.target.value;
this.setState({value: city})
}
Then, in your render:
render() {
return <div>
<input onChange={this.handleValueChange} value={this.state.value}/>
<SaveWeather city={this.state.value} />
</div>
}
You should try to understand this code, don't just copy it. Any question drop a comment below ;)

Not able to edit content in textbox when it is derived from this.props.location.state

I am working on Update Task activity, and I am not able to edit content in Textbox as it has been derived from this.props.location.state.****.
Please suggest. How I can keep it editable.
Code :
<textarea ref="taskdescr" type="text" class="form-control" value={this.props.location.state.tskDescr} id="taskDesc"></textarea>
This is normal because when you provide value props in react.
The textarea will gain the value your provided after each render.
So because the value of your props does not change when you edit the textarea, the provided value is always the same.
To make your textarea editable your need to use the state instead of props.
If i understand what you want to do your state can look like site
{
tskDescr: '',
proptskDescr: '',
}
Use getDerivedStateFromProps and update tskDescr when proptskDescr is not equal to props.location.state.tskDescr.
And add an onChange event on the textarea to update the tskDescr
You need to change the value on the state using onChange for the textarea as follows
constructor(props){
super(props);
this.state = {
location: {
tskDescr: ''
}
}
}
And then
handleChange = (event) => {
this.setState({
location: {
tskDescr: event.target.value
}
});
}
Props are immutable use state if you want to mutate the values, after adding the state you need to provide a listener so that you can change the value.
<textarea
value={this.state.text}
onChange={this.handle}
/>
handle = ({target:{value}}) => this.setState({text:value});

Data is not updated when using as object, but changes normally when it's a variable

I'm writing a function to update a custom checkbox when clicked (and I don't want to use native checkbox for some reasons).
The code for checkbox is
<div class="tick-box" :class="{ tick: isTicked }" #click="() => isTicked = !isTicked"></div>
which works find.
However, there are so many checkboxes, so I use object to keep track for each item. It looks like this
<!-- (inside v-for) -->
<div class="tick-box" :class="{ tick: isTicked['lyr'+layer.lyr_id] }" #click="() => {
isTicked['lyr'+layer.lyr_id] = !isTicked['lyr'+layer.lyr_id]
}"></div>
Now nothing happens, no error at all.
When I want to see isTicked value with {{ isTicked }}, it's just shows {}.
This is what I define in the <script></script> part.
export default {
data() {
return {
isTicked: {},
...
};
},
...
}
Could you help me where I get it wrong?
Thanks!
Edit:
I know that declaring as isTicked: {}, the first few clicks won't do anything because its proerty is undefined. However, it should be defined by the first/second click not something like this.
Objects does not reflect the changes when updated like this.
You should use $set to set object properties in order to make them reactive.
Try as below
<div class="tick-box" :class="{ tick: isTicked['lyr'+layer.lyr_id] }" #click="onChecked"></div>
Add below method:
onChecked() {
this.$set(this.isTicked,'lyr'+this.layer.lyr_id, !this.isTicked['lyr'+this.layer.lyr_id])
}
VueJS watches data by reference so to update object in state you need create new one.
onChecked(lyr_id) {
const key = 'lyr'+lyr_id;
this.isTicked = {...this.isTicked, [key]: !this.isTicked[key]};
}

Categories