Trying to emit result from bus vuejs - javascript

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 )

Related

How to retrieve value passed to a prop from an array VueJS/NuxtJs

CodeSandbox
Reference Video
I'm trying to retrieve a value that was dynamically created and passed to a prop.
After clicking "add card" and clicking on one of the created cards, the goal is to pass the value(prop: randomNum) into a variable(num1).
In the Sandbox, I'm able to get the value of the Id which was also created dynamically.
methods: {
//"emits" or Allows value of the id prop in Array to be reached from parent?
select() {
this.$emit("select", this.id);
}
Above Code from nested /component/card.vue
<card
v-for="cards in cardArray"
:key="cards.id"
:id="cards.id"
:randomNum="cards.randomNum"
#select="selectCard"
:selectedCard="selectedCard"
:playable="cards.playable"
:visable="cards.visable"
></card>
<h1>{{num1}}</h1>
<h1> {{ selectedCard }} </h1>
----------
data() {
return {
cardArray: [],
selectedCard: null,
num1: null,
----------
methods: {
selectCard(cards) {
this.selectedCard = cards;
}
Above code from the main /component/hand.vue
From my understanding, in this case, cards evaluates to this.id?
How can I set num1 to equal cards.randomNum(second item in payload)
The same way that selectedCard evaluates to cards(cards.id)
I've tried variations of "item.Array" and using $emit on this.randomNum the same way it was used to $emit this.Id which doesn't work, how can I properly do this?
//in card hand componenet
select() {
this.$emit("select", this.id);
this.$emit("select", this.randomNum);
}
//in card hand componenet
selectNum(cards.randomNum) {
this.num1 = randomNum;
You can either use two separate events or just pass an object with multiple values.
two events:
select() {
this.$emit("selectId", this.id);
this.$emit("selectNum", this.randomNum);
}
or pass object with multiple values
this.$emit("select", {id:this.id, randomNum:this.randomNum});

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

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
}
}
}

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]};
}

Can I get computed data from child component to parent component?

Is there any way to get computed data from child component to parent component? Because I'm sending the data from parent to child first and then I want to use the comuted property (data) in the parent component. I want to do that because I want to reuse that important component (child) in other components too.
I have a search input field for filtering my items, and when i wrote something down i wanna get back that list from the child component.
Parent component
<input class="form-control form-control-search m-input" autocomplete="off" type="text" v-on:input='testFunc()' v-model="search" placeholder="Search...">
<paginate-links v-if="items.length > 0" :models="items">
<div class="m-list-timeline__item no-timeline" v-for="item in filterItems" v-bind:key="item.id">
{{ item.title }}
</div>
</paginate-links>
Child component
props: ['item']
computed: {
filterItems () {
return filter // here goes my code
}
}
So can i get the filterItems in the parent component?
There are a couple ways you can send data back to the parent, but probably the easiest way I'd say is using an emit within the computed.
in child:
computed:{
myVal() {
let temp = this.num + 1;
this.$emit('onNumChange', temp);
return temp;
}
}
in parent template:
<my-child-component #onNumChange="changeHandler"/>
in parent script
methods: {
changeHandler(value) {
console.log('Value changed to: ', value);
}
}
You could do a similar thing with watch, or pass a function from parent as a prop that notifies the parent, but my recommended way would be to use vuex
https://vuex.vuejs.org/en/

React-select Creatable component not acting in default manner

This is my Creatable component:
function optionsForSelect(field) {
return field
.values
.map((fieldOption) => {
return {value: fieldOption, label: fieldOption};
});
}
function PatientSelectInput({field, options, value, onChange, disabled}) {
const className = field.id + '-select';
return (
<label className={cx('input-label', className)}>
<div className="label-text">{field.displayName}</div>
<Creatable
value={value}
onChange={(selectedValue) => onChange(selectedValue ? selectedValue.value : null)}
disabled={disabled}
onBlurResetsInput={false}
onCloseResetsInput={false}
options={options} />
</label>
);
}
It is a functional component. When it renders, I can create a new option but when I hit tab or enter or click on the automatically generated "Create Option..." the newly created option disappears. I just want the default behavior.
What am I missing?
Unfortunately this version of react-select mutates it's options prop.
This is very bad coding and is fixed in version 2: https://github.com/JedWatson/react-select/issues/2484
The issue you're experiencing is coming from the way you construct the options. Your function creates a new object on each render, replacing the array containing the option you just created.
If you can't update your version I suggest you save the output of optionsForSelect to this.state.options, then pass the state variable into react-select. The upshot of doing this is you still have the ability to mutate the state of your element and limits the impact of the mutate.

Categories