VeeValidate scoped validation doesn't work - vuejs2

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/

Related

Can you add more than one modifier to vue js v-model?

Can you add more than one modifier?
For example:
<input v-model.trim="name.first"/>
<input v-model.lazy="name.first"/>
into something along the line
<input v-model.{lazy,trim}="name.first"/>
Possible or not possible?
Yes!
followup question:
What is the concept behind it?
I understand it works,
but ".lazy.trim" sounds like trim is a part of lazy object
Yes,we can add more than one modifier to Vue js v-model.
new Vue({
'el': '#app',
data: {
val: 'default value',
num: 0,
trimExample: ''
},
methods: {
handleBtnClick() {
console.log(this.trimExample, this.num)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model.lazy="val">
<br> {{val}}
<br><br>
<input type="text" v-model.number="num">
<input type="text" v-model.trim.lazy="trimExample">
<button v-on:click="handleBtnClick">
check console
</button>
</div>
Fiddle Link
Yes you can use it !
for example you can see this link which v-model.trim.lazy is used in it !
https://vuelidate.js.org/#sub-basic-form
<input class="form__input" v-model.trim.lazy="$v.age.$model"/>

Vue-JS v-show Percitence in radio

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>

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");
}
}
});

Validation Error messages on form load

About the issue
I am using Laravel 5.6.7 with vue.js. vee-validate is being used for validation
When the form loads, it shows validation error messages. User did not even click the submit button. Below is the screenshot.
Code
<template>
<div>
<form role="form">
<input v-validate data-vv-rules="required" type="text"
v-model="UpdateForm.First_Name">
<p v-if="errors.has('First Name')">{{ errors.first('First Name') }}</p>
<button type="button">
Update Profile
</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
UpdateForm: {
First_Name: ''
}
}
},
created() {
this.GetProfile();
},
methods: {
GetProfile() {
axios.post("some api url", {}).then(response => {
this.UpdateForm.First_Name = response.data.Data.First_Name;
});
}
}
}
</script>
Could I get rid of validation error messages on form load?
This is not the expected behavior. For initial validating you need to inform it with v-validate.initial.
Maybe you are defining this to happen when declaring v-validate or in other place.
Vue.use(VeeValidate);
new Vue({
el: '#demo'
})
.is-danger{
color: red;
}
<script src="https://unpkg.com/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vee-validate#latest/dist/vee-validate.js"></script>
<div id="demo">
<label>This one needs touching</label>
<input type="text" name="name" v-validate="'required'">
<div v-show="errors.has('name')" class="is-danger">Errors: {{ errors.first('name') }}</div>
<br/>
<label>This one does not need touching</label>
<input name="name2" v-validate.initial="'required'" type="text">
<div v-show="errors.has('name2')" class="is-danger">{{ errors.first('name2') }}</div>
</div>
Changed
this.editForm.First_Name = Data.User.First_Name;
to
if(Data.User.First_Name != null && Data.User.First_Name != "") {
this.editForm.First_Name = Data.User.First_Name;
}
and validation is working fine now. Basically the variable is not initialized.

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