Compute not updating if radio is checked through code - vue.js

Sorry for the title, didn't know how I could better explain it.
I have 3 radio buttons. The output should be the value of whichever radio button is selected. Except, the 3rd radio button has a textarea, and if that radio button is selected, the resulting output should be what is in the textarea. I've also linked the #click of textarea to automatically check the radio button, so if a user clicks on the textarea, that radio button gets checked automatically.
Everything works, if you see the JSFiddle code and click on the 3rd radio button and type text in the textarea, the output should work fine. But if you refresh the page, and instead of clicking the radio button, if you directly click on the textarea (which indirectly checks the radio button), writing in the textarea will not update the output. Which means the compute function is not working.
However if you click on another radio button and then click back on the 3rd radio button, things will start working fine.
Here's the code (JSFiddle here: https://jsfiddle.net/eywraw8t/43651/ )
<div id="app">
Selected option 1: {{ selectedText }}
<BR>
<input type="radio" name="item1" value="" v-model="selected[0]" /> Empty<BR>
<input type="radio" name="item1" value="Hi" v-model="selected[0]" /> Hi<BR>
<input type="radio" name="item1" value="**custom**" v-model="selected[0]" /> <textarea name="textarea0" id="textarea0" cols="30" rows="10" v-model="custom[0]" #click="customClicked"></textarea><BR>
</div>
<script>
new Vue({
el: "#app",
data: {
selected: ['', ''],
custom: ['', ''],
},
computed: {
selectedText: function() {
if (this.selected[0] != '**custom**') return this.selected[0];
return this.custom[0];
}
},
methods: {
customClicked: function(e) {
//$(e.target).prev().prop('checked', true);
this.selected[0] = '**custom**';
this.$forceUpdate();
}
}
})
</script>

You don't need jQuery for this - just bind the values to one array and use #focus event on the textarea:
new Vue({
el: "#app",
data: {
values: ['', 'Hi', ''],
selected: '',
},
computed: {
selectedText: function() {
return this.values[this.selected];
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
Selected option 1: {{ selectedText }}
<BR/>
<input type="radio" name="item1" value="0" v-model="selected" /> Empty
<BR/>
<input type="radio" name="item1" value="1" v-model="selected" /> Hi
<BR />
<input type="radio" name="item1" value="2" v-model="selected" />
<textarea name="textarea0" id="textarea0" cols="30" rows="10" v-model="values[2]" #focus="selected = '2'"></textarea>
</div>
Also the JSFiddle.

Update
As the questioner choose another answer, but i don't think that is the key point.
The reason is $forceUpdate() only force the view to re-render, not the computed properties. See the issue forceUpdate does not update computed fields
And i create a simple jsfiddle to descript it.
Raw Answer
The problem you faced is that vuejs cannot detect array change in some situation. You can see more detail in the doc vuejs list rendering
So the solution is replaced these code
this.selected[0] = '**custom**';
this.$forceUpdate();
with
this.$set(this.selected, 0, '**custom**')

While writing the answer I got an idea and it seems to work. However I still want to know if there is a "right" way of doing things which I'm missing.
The fix was to "click" the radio button instead of "checking" it:
$(e.target).prev().click();

Related

How to check radio button in Vue

I have a custom radio list with hidden input radio fields. Whenever I click on my custom radio button I want the hidden radio button to be checked. My custom radio button has a #click function and in that function I have the following code:
customRadioHandler(key) {
this.$refs[key].checked;
}
The key parameter is the same as the ref in the hidden radio button. This code does not seem to check the hidden radio button. Does anyone know how to fix this?
<template>
<div id="app">
<label>
<input type="radio" #click="handleFirstRadioClick" />
test1
</label>
<label class="radio-2">
<input type="radio" :checked="secondRadioValue" />
test2
</label>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
secondRadioValue: false,
};
},
methods: {
handleFirstRadioClick() {
return (this.secondRadioValue = true);
},
},
};
</script>
Full example:
https://codesandbox.io/s/amazing-dust-hen6w?file=/src/App.vue

VueJs handle Form Submit on Enter press

I have a background in Angular. Starting with vue was an okay experience for me until I came across a problem which VueJS developers seem to have shit on and slid under the carpet.
How can we create a form in which user can press enter from an input field to submit the form
This was seriously disappointing.
and please if you know the answer be kind enough to post in the Official vue documentation as well.
*Note:
my workaround: I used v-on:keydown.enter.prevent='loginUser' on every input field.
is there any way to not use it this way ( on every input field).
With button type as submit as well, form gets submitted when Enter key is pressed on any input.
No explicit binding is required on keypress.
new Vue({
el: '#app',
data() {
return {
title: "Vue 2 -Form submission on Enter",
formInput:{
fname:'',
lname:'',
gender:'male'
}
};
},
methods:{
onSubmit(){
console.log('submitted', this.formInput)
}
}
})
.form{
display:flex;
flex-direction:column;
gap:10px;
max-width:200px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"><h2>{{title}}</h2>
<form v-on:submit.prevent="onSubmit" class="form">
<input v-model="formInput.fname" #keydown.enter.prevent placeholder="first name"/>
<input v-model="formInput.lname" placeholder="last name"/>
<div>
<input v-model="formInput.gender" name="gender" placeholder="Gender" type="radio" value="male"/>male
<input v-model="formInput.gender" name="gender" placeholder="Gender" type="radio" value="female"/>Female
</div>
<button type="submit">Submit</button>
<pre>{{formInput}}</pre>
</form>
</div>
I'd urge you to check out this page on the VueJS documentation.
It explains how to set events on certain interactions. For example, you can trigger a function call on pressing the Enter key within an input field by doing this:
<input type="text" #keyup.enter="submit">
This will call the submit() method when the Enter key is pressed and released (keyup). On press, you can use keydown instead.'
In fact, the example I've taken is directly from this section in the page I linked above.
EDIT: A pure-HTML way to do this is to set your input type as submit, which will allow Enter to submit the form
You need to wrap your inputs within <form> ... </form> tag

How do I keep two identical Vue components in sync with each other?

I'm trying to make two radio button clusters on the same page (but inside different drawers) keep in sync with each other. This worked before I broke them out into a component.
Now my component looks like:
Vue.component('radio-cluster', {
props: ['value'],
template: `
...
<input type="radio" name="radio" value="Hour" :checked="value == 'Hour'" #input="$emit('input', $event.target.value)">
<input type="radio" name="radio" value="Week" :checked="value == 'Week'" #input="$emit('input', $event.target.value)">
...
`,
});
The Vue instance:
new Vue({
el: '#app',
data: function () {
return {
timePeriod: "Hour"
}
});
And the relevant HTML:
<div id="app">
<radio-cluster v-model="timePeriod"></radio-cluster>
...
<radio-cluster v-model="timePeriod"></radio-cluster>
</div>
The behavior I'm getting is odd. It will correctly show in one of the two clusters, and emit the correct event from either one. But the other component ignores the event from the one I clicked on. How can I make Vue take the value updated in one component and automatically give it to the other every time either one updates?
Seems like the cause of you problem is the same name for each input.
From documentation
A radio group is defined by giving each of radio buttons in the group the same name. Once a radio group is established, selecting any radio button in that group automatically deselects any currently-selected radio button in the same group.
Try to remove name attribute:
<input type="radio" value="Hour" :checked="value == 'Hour'" #input="$emit('input', $event.target.value)">
<input type="radio" value="Week" :checked="value == 'Week'" #input="$emit('input', $event.target.value)">
if name attribute is required use different names for each component.
Thanks #Ferrybig for the comment.

What is the proper way to add same component everytime a button is clicked?

I am creating a form which needs to have a button and when clicked a new component which is only an input field would be added.
For example I have only one field to put in my address, but my adress is very long so I need 2 additional input fields. I click a button twice and two more components with the input appear.
What is a proper way to achieve this functionality?
Here is a simple solution to your question.
https://jsfiddle.net/RiddhiParekh/usd57zkb/6/
Template =>
<div id="vue-instance">
<button #click="addInput">Click to add input</button>
<br>
Address:
<div v-for="i in count">
<input type="text">
</div>
</div>
Script =>
var vm = new Vue({
el: '#vue-instance',
data: {
count:1
},
methods:{
addInput(){
this.count = this.count+1
}
}
});

How do I use labels for a set of dynamically created radio buttons?

I've got a dynamically created radio button list in Vuejs 2:
<div class="form-check" v-for="design in designs" :key="design.id">
<input class="form-check-input" type="radio" :value="design" v-model="selectedDesign" :id="design.id" :name="design.id">
<label class="form-check-label" v-bind:for="design.id">{{design.name}}</label>
</div>
however, that "v-bind:for" doesn't work -- and hardcoding a "for" wouldn't let me link to the dynamically created radio button.
Does anyone know how to use a "for" in a dynamically created label?
Your snippet works.
Double check your elements' ids, you probably have duplicated ids, causing the labels to malfunction.
Check the demo below. There are two radios that work (so you know your snippet works) and one that doesn't (because there is a duplication: a <div> with the same id of the radio).
new Vue({
el: '#app',
data: {
designs: [
{id: 1, name: "I'm the label for the radio with id=1 (will work)"},
{id: 2, name: "I'm the label for the radio with id=2 (won't work because another element with id=2 exists)"},
{id: 3, name: "I'm the label for the radio with id=3 (will work)"}
],
selectedDesign: null
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.min.js"></script>
<div id="app">
<div id="2">I am a div with id=2.</div>
<br><br>
<div class="form-check" v-for="design in designs" :key="design.id">
<input class="form-check-input" type="radio" :value="design" v-model="selectedDesign" :id="design.id" :name="design.id">
<label class="form-check-label" v-bind:for="design.id">{{design.name}}</label>
</div>
</div>