Vue-JS v-show Percitence in radio - vue.js

v-show appears not be percitent when whit radio (v-model)
Please find example: https://jsfiddle.net/Lngocxrj/1/
<div id="helloWorldApp">
<input type="radio" v-model="visible" value="true" name="optradio">hide
<input type="radio" v-model="visible" value="false" name="optradio">show
<div v-show="visible">
Hello World
</div>
<p>
{{visible}}
</p>
</div>
new Vue({
el: "#helloWorldApp",
data: {
visible: true
},
methods: {
show: function() {
this.visible = !this.visible;
}
}
});

It works, if you use a method to toggle the data.
HTML:
<div id="helloWorldApp">
<label>hide<input type="radio" value="false" #click="inputClick(false)" name="optradio" /></label>
<label>show<input type="radio" value="true" #click="inputClick(true)" name="optradio" /></label>
<div v-show="visible">
Hello World
</div>
<p>
{{visible}}
</p>
</div>
JavaScript:
new Vue({
el: "#helloWorldApp",
data: {
visible: false
},
methods: {
inputClick(val) {
this.visible = val;
}
}
});

Added a new property to differentiate input changes and show/hide div
<div id="helloWorldApp">
<input type="radio" v-model="visible" value="true" name="optradio">hide
<input type="radio" v-model="visible" value="false" name="optradio">show
<div v-if="showDiv">
Hello Worlds
</div>
<p>
{{visible}}
</p>
</div>
new Vue({
el: "#helloWorldApp",
data: {
visible: false,
showDiv: true
},
watch: {
visible(val) {
this.showDiv = val;
}
}
});

As per my comment: the reason why your element is showing regardless of the v-show directive is because the values from the checkboxes are being stored as strings and not booleans. And since "false" is actually truthy because it is a string of non-zero length, your div will always be visible.
Quick solution: Perform string comparison
If you want to keep your code as-is, and understanding that you are looking at string values instead of boolean stored in visible, updating your template to use v-show="visible === 'true'" will work.
Note: I do not encourage this method though, because this is a code smell (see further below for a better solution).
new Vue({
el: "#helloWorldApp",
data: {
visible: 'true'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="helloWorldApp">
<input type="radio" v-model="visible" value="true" name="optradio">hide
<input type="radio" v-model="visible" value="false" name="optradio">show
<div v-show="visible === 'true'">
Hello World
</div>
<p>
{{visible}}
</p>
</div>
A better solution: use checkbox for binary state toggling
This brings us to another issue: since you are toggling a property, a radio button is not the best UI to do that. A checkbox is more appropriate: in this case, you don't need to do dirty strict comparisons:
new Vue({
el: "#helloWorldApp",
data: {
visible: true
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="helloWorldApp">
<input type="checkbox" v-model="visible" checked>visible
<div v-show="visible">
Hello World
</div>
<p>
{{visible}}
</p>
</div>

Related

Vue.js adding a toggle and method to a button

I have a button that should toggle and also call a method. How do I achieve this? Seems like it can be only one or the other.
new Vue({
el: "#app",
data: {
iExist:false,
iDoNotExist: true,
},
methods: {
iSignedUpforThis: function(){
console.log("step X");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-show="iExist"> i EXISTS </p>
<p v-show="iDoNotExist">
<strong> You are not found: </strong>
<form >
First name:<br>
<input type="text" name="firstname" value="Mickey">
<br>
Last name:<br>
<input type="text" name="lastname" value="Mouse">
<br><br>
</form>
<BUTTON v-on:click="iExists = iDoNotExist">
TOGGLE MY EXISTENCE
</BUTTON>
</div>
Move
iExists = iDoNotExist to a method:
methods: {
iSignedUpforThis: function(){
this.iExist = this.iDoNotExist
console.log("step X");
}
}
<button v-on:click="iSignedUpForThis">
TOGGLE MY EXISTENCE
</button>
First off to accomplish your desired result you need only one Boolean variable. Then in your method just switch between true and false. Also you have an invalid markup - there is closing tap p but no closing. That's why your example does not work.
Notice: it's bad idea to nest form tag inside p tag, so use div instead. It's considered a good practice to associate your input with it's label using label tag. Also there is shortcut for v-on:click - #click. data should be an function that returns an object, this will prevent . multiple instance to share the same object.
If you follow above recommendations you will make your code much clear and bug-less.
new Vue({
el: '#app',
data: {
isExist: false,
},
methods: {
method() {
this.isExist = !this.isExist
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-show="isExist">I exist</div>
<div v-show="!isExist">
<strong>You are not found:</strong>
<form>
<label>First name:<br>
<input type="text" name="firstname" value="Mickey">
</label>
<br>
<label>Last name:<br>
<input type="text" name="lastname" value="Mouse">
</label>
</form>
</div>
<button #click="method">Toggle</button>
</div>
It might be late but I am sure it will help others. Create a component ToggleButton.js and paste the below codes.
<template>
<label for="toggle_button">
<span v-if="isActive" class="toggle__label">On</span>
<span v-if="! isActive" class="toggle__label">Off</span>
<input type="checkbox" id="toggle_button" v-model="checkedValue">
<span class="toggle__switch"></span>
</label>
</template>
<script>
export default {
data() {
return {
currentState: false
}
},
computed: {
isActive() {
return this.currentState;
},
checkedValue: {
get() {
return this.defaultState
},
set(newValue) {
this.currentState = newValue;
}
}
}
}
</script>
Take a look at the article to learn more https://webomnizz.com/create-toggle-switch-button-with-vue-js/

How to combine v-for and v-model to edit a list of objects?

I would like to edit a list of users using Vue.js. Each user has a name and an age. It seems that v-for is the right directive to work with lists and v-model is the right directive to bind the contents of an input to a particular element in the list.
So I tried to implement it like this:
new Vue({
el: '#exercise',
data: {
users: [{
name: "martin",
age: 32
}]
},
methods: {
add_user: function() {
this.users.push({
name: "",
age: ""
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="exercise">
<form id="list">
<div></div>
<div v-for="user in users">
<input v-model="user.name">
<input v-model="user.age">
<button #click="add_user">+</button>
</div>
</form>
</div>
However, once I click the button to add a new user, a new line with input fields is displayed only for a fraction of a second and then disappears, leaving the list of users containing only one element.
Please, could you tell me what am I doing wrong?
The reason is <button> with <form> when you're click button it does the request automatically so its refresh or disappear because it fails to try to use <a> or using <button #click.prevent="add_user" />
<div id="exercise">
<form id="list">
<div></div>
<div v-for="user in users">
<input v-model="user.name">
<input v-model="user.age">
<a #click="add_user">+</a> //solution
<button #click.prevent="add_user">+</button> //another solution
</div>
</form>
</div>
The button is submitting the form, add .prevent to stop the action:
new Vue({
el: '#exercise',
data: {
users: [{
name: "martin",
age: 32
}]
},
methods: {
add_user () {
this.users = [ ...this.users, {
name: "",
age: ""
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="exercise">
<form id="list">
<div v-for="(user, index) in users" :key="index">
<input v-model="user.name" :key="`name-${index}`">
<input v-model="user.age" :key="`age-${index}`">
</div>
<button #click.prevent="add_user">+</button>
</form>
</div>
You need to set tye type of the button, since it's inside a form it fallbacks to submit button.
Also, it's good to define a :key to help vue to tell the difference between one line to another on v-for.
new Vue({
el: '#exercise',
data: {
users: [{
id:new Date().getTime(),
name: "martin",
age: 32
}]
},
methods: {
add_user: function() {
this.users.push({
id:new Date().getTime(),
name: "",
age: ""
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="exercise">
<form id="list">
<div v-for="user in users" :key="user.id">
<input v-model="user.name">
<input v-model="user.age">
<button type="button" #click="add_user">+</button>
</div>
</form>
</div>

VeeValidate scoped validation doesn't work

I'm building a web application which has several forms on the same page, so scoped validation from VeeValidate looked like an obvious choice, but I can't make it work.
No matter what I do, model is always valid. I built a small example to help you help me 😊 https://jsfiddle.net/pvkovalev/3vwp9zdo/
Here is my HTML code:
<div id="app">
<div data-vv-scope="InformationStep1">
<input v-model="input1" v-validate="{required: true}" />
</div>
<div data-vv-scope="InformationStep2">
<input v-model="input2" v-validate="{required: false}" />
</div>
<input type="button" #click="validate" value="validate" />
</div>
And JS:
Vue.use(VeeValidate)
new Vue({
el: "#app",
data: {
input1: undefined,
input2: 'not required'
},
methods: {
validate: function (){
this.$validator.validateAll('InformationStep1').then((result) => {
alert('InformationStep1 valid: '+ JSON.stringify(result))
})
this.$validator.validateAll('InformationStep2').then((result) => {
alert('InformationStep2 valid: '+ JSON.stringify(result))
})
}
}
})
What did I miss here? Perhaps, something obvious. Any help appreciated!
I found it!
Okay, so the solution is to use form tag, not a div tag. As soon as I changed it, it works like a charm.
Here is a new code:
<div id="app">
<form data-vv-scope="InformationStep1">
<input v-model="input1" v-validate="{required: false}" />
</form>
<form data-vv-scope="InformationStep2">
<input v-model="input2" v-validate="{required: true}" />
</form>
<input type="button" #click="validate" value="validate" />
</div>
and new fiddle https://jsfiddle.net/pvkovalev/1L5t3dsc/

Binding vue components to class name

Alright so I am trying to bind this vue components to a class name so it triggers on every element that has this class but what happens is that it only works with the first element and not with other ones
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" /> <!-- submit comment being only triggered on this one -->
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
As you can see above, I've got 3 divs with class __comment_post so naturally submitComment should be bound to all these 3 divs but what happens is that submitComment is being triggered only on the first one
var app = new Vue({
el:".__comment_post",
data: {
comment: ""
},
methods: {
submitComment: function() {
console.log("Test");
}
}
});
Here is a little example you and others can follow in order to bind vue instance to class names.
Lets say, you would like to bind Vue to multiple existing <div class="comment"> element in HTML.
HTML:
<div class="comment" data-id="1">
<div>
<div class="comment" data-id="2">
<div>
Now, you can try the following logic/code to your example.
JS:
var comments = {
"1": {"content": "Comment 1"},
"2": {"content": "Comment 2"}
}
$('.comment').each(function () {
var $el = $(this)
var id = $el.attr('data-id')
var data = comments[id]
new Vue({
el: this,
data: data,
template: '<div class="comment">{{ content }}<div>'
})
})
I hope this will answer your question :)
The vue instance is mounted on the first found DOM element with the css selector passed to the el option. So the rest two div have no vue instances mounted on them.
So wrap your divs with a wrapper div and mount the vue instance on that wrapper
<div id="app">
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" /> <!-- submit comment being only triggered on this one -->
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
script
var app = new Vue({
el:"#app",
data: {
comment: ""
},
methods: {
submitComment: function() {
console.log("Test");
}
}
});

vue js get multiple values from inputs

So I have 2 blocks of HTML, each containing 2 input fields and when submitting the form, I want to get all values from the inputs, and then create an object from the values...
As of know I've done it with plain vanilla JS and it works as it should, however if feels like to touching the DOM a bit to much, and also are very much depending on a specific DOM struckture, and therefore I was thinking there must be a better way, the VUE way so to speak, however im a bit stuck on how to do this the VUE way, which is why posting the question here in hope of getting some useful tips :)
HTML:
<form novalidate autocomplete="off">
<div class="input-block-container">
<div class="input-block">
<input type="text" placeholder="Insert name" name="name[]" />
<input-effects></input-effects>
</div>
<div class="input-block">
<input type="email" placeholder="Insert email address" name="email[]" />
<input-effects></input-effects>
</div>
</div>
<div class="input-block-container">
<div class="input-block">
<input type="text" placeholder="Insert name" name="name[]" />
<input-effects></input-effects>
</div>
<div class="input-block">
<input type="email" placeholder="Insert email address" name="email[]" />
<input-effects></input-effects>
</div>
</div>
<button class="button button--primary" #click.prevent="sendInvites"><span>Send</span></button>
</form>
JS:
methods: {
createDataObject() {
let emailValues = document.querySelectorAll('input[type="email"]');
emailValues.forEach((email) => {
let name = email.parentNode.parentNode.querySelector('input[type="text"]').value;
if(email.value !== "" && name !== "") {
this.dataObj.push({
email: email.value,
name
});
}
});
return JSON.stringify(this.dataObj);
},
sendInvites() {
const objectToSend = this.createDataObject();
console.log(objectToSend);
//TODO: Methods to send data to server
}
}
You can provide data properties for each of your inputs if you have static content.
data: function() {
return {
name1: '',
email1: '',
name2: '',
email2: ''
}
}
Then use them in your template:
<input type="text" placeholder="Insert name" v-model="name1" />
Access in method by this.name1
Try this
<div id="app">
<h1> Finds </h1>
<div v-for="find in finds">
<input name="name[]" v-model="find.name">
<input name="email[]" v-model="find.email">
</div>
<button #click="addFind">
New Find
</button>
<pre>{{ $data | json }}</pre>
</div>
Vue Component
new Vue({
el: '#app',
data: {
finds: []
},
methods: {
addFind: function () {
this.finds.push({ name: '', email: '' });
}
enter code here
}
});