Custom Rules in Kendo Ui For Vue Validator - vue.js

I start using Kendo UI for Vue.
I want to use Kendo Validator Wrapper and it seems to be very simple for basic validation.
The sample provided:
<div id="vueapp" class="vue-app">
<form id="ticketsForm" ref="myForm" v-kendo-validator #submit.prevent="onSubmit">
<input type="search"
id="search"
name="search"
required
validationMessage="Field is required"
class="k-textbox" />
<span class="k-invalid-msg" data-for="search"></span>
<div>
<button class="k-button k-primary" type="submit">Submit</button>
</div>
</form>
<div class="status"></div>
For custom validation Kendo provide rules in configuration section:
https://docs.telerik.com/kendo-ui/api/javascript/ui/validator/configuration/rules
... you can help me, please, to understand how I can setup custom rules with this wrapper?
Thank-you

You can define the validator this way - v-kendo-validator="getRules()" and return the object of the rules in the method getRules. Here is a plunked.
getRules: function () {
return {
rules: {
customRule1: function (input) {
// all of the input must have a value
return kendo.jQuery.trim(input.val()) !== ''
},
customRule2: function (input) {
// only 'Tom' will be valid value for the username input
return input.val() === 'Tom'
}
},
messages: {
customRule1: 'All fields are required',
customRule2: 'Your UserName must be Tom'
}
}
}

Related

change data variable when passed into a method in vue

I have the following vue component where I am changing the class of the parent row based on whether or not an input is focused
<template>
<div class="form form--login">
<div class="form__row" :class="{entered: emailEntered}">
<label class="form__label" for="login-form-email">Email address</label>
<input type="text" class="form__control form__control--textbox" name="email-address" id="login-form-email" #focus="emailEntered = true" #blur="handleBlur($event, emailEntered)">
</div>
<div class="form__row" :class="{entered: passwordEntered}">
<label class="form__label" for="login-form-password">Password</label>
<input type="password" class="form__control form__control--textbox form__control--password" name="password" id="login-form-password" #focus="passwordEntered = true" #blur="handleBlur($event, passwordEntered)">
</div>
</div>
</template>
<script>
export default {
name: 'login-form',
data() {
return {
emailEntered: false,
passwordEntered: false,
}
},
methods: {
handleBlur(e, enteredBool) {
if (e.currentTarget.value.trim() === '') {
// this doesn't do anything - I can do an if else statement to change this.passwordEntered or this.emailEntered based on the name of the current target, but how do I change the value by passing it into the method?
enteredBool = false;
}
},
}
}
</script>
but it doesn't seem to change the variable that is passed into the method - how do I pass a data variable into the method and change it's value? Or should I be doing it in a different way? I don't really want to be doing an if else statement as I may have a form that has got a lot more inputs on and I think that would be really inefficient to maintain
I also thought that I could do something in the #bur as you can do #blur="passwordEntered = false", but I wasn't sure how to check if the field was empty or not
In order to change the variable, you need to refer it using this
handleBlur(e, enteredBool) {
if (e.currentTarget.value.trim() === '') {
this[enteredBool] = false; //Change added
}
},
and the way you pass it should be like
#blur="handleBlur($event, 'emailEntered')" //Added single quotes
and
#blur="handleBlur($event, 'passwordEntered')" //Added single quotes

Problem with checking ID in filter function inside a method

I work on CRUD based on Vuejs. I have three components Goods.vue, UpdateGood.vue, App.vue
My Problem is in the "updateGood" method inside App.vue . I have to click the "update" button twice to update my current good item in table.
First click is for filter function to get the current item.
Second click is for splice function to replace updated item with old one. I want to implement the filter function in my editGood() method for getting the id of current item, but don't know how to do it.
I tried to set the filter function in editGood() method where I take current object by clicking the button
App.vue file:
template:
<template>
<div id="app">
<!-- Component for adding a good -->
<AddGood v-on:add-good="addGood"/>
<!-- Component for managing goods in table -->
<Goods v-bind:goods="goods" v-on:del-good="deleteGood" v-on:edit-good="editGood"/>
<UpdateGood v-bind:good="goodToUpdate" v-on:update-good="updateGood" />
</div>
</template>
script:
import Goods from './components/Goods.vue';
import AddGood from './components/AddGood.vue';
import UpdateGood from './components/UpdateGood.vue';
methods : {
addGood(newGood){
this.goods = [...this.goods,newGood];
},
deleteGood(id){
this.goods = this.goods.filter(good => good.id !== id);
},
editGood(good){
this.goodToUpdate = Object.assign({}, good);
},
updateGood(updatedGood){
// Creating new array with only one object inside
e
let goodOriginalIndex = this.goods.indexOf(goodOriginalObject[0]);
this.goods.splice(goodOriginalIndex, 1, updatedGood);
alert("Js"+JSON.stringify(updatedGood));
}
}
Goods.vue:
<tr v-bind:key="good.id" v-for="good in goods"
v-on:del-good="$emit('del-good',good)">
<td>{{good.id}}</td>
<td>{{good.date}}</td>
<td>{{good.name}}</td>
<td>{{good.price}}</td>
<td>{{good.amount}}</td>
<td>{{good.sum}}</td>
<td><input type="submit" value="Удалить"
#click="$emit('del-good',good.id)"/></td>
<td><input type="submit" value="Изменить"
#click="$emit('edit-good',good)"/></td>
</tr>
UpdateGood.vue
template:
<div class="form-style-6">
<h1>Текущий Товар</h1>
<form #submit="updateGood">
<input type="text" v-model="good.id" placeholder="Артикул" />
<input type="text" v-model="good.date" placeholder="Дата Поступления" />
<input type="text" v-model="good.name" placeholder="Название" />
<input type="text" v-model="good.price" placeholder="Цена" />
<input type="text" v-model="good.amount" placeholder="Количество" />
<input type="text" v-model="good.sum" placeholder="Сумма" />
<input type="submit" value="Обновить" class="btn" />
</form>
</div>
script:
<script>
export default {
name : 'UpdateGood',
props: ["good"],
methods : {
updateGood: function(e){
e.preventDefault();
// Send up to parent
this.$emit('update-good', this.good);
}
}
}
</script>
I have to get the id of object in the editGood() method which pass this id to updateGood() function where splice is impelemented.
You can use watch function to subscribe when goodToUpdate object was updated and call updateGood function after that.
// your main component
{
methods: {
updateGood(updatedGood){
// Creating new array with only one object inside
let goodOriginalIndex = this.goods.indexOf(goodOriginalObject[0]);
this.goods.splice(goodOriginalIndex, 1, updatedGood);
alert("Js"+JSON.stringify(updatedGood));
this.goodToUpdate = null // reset after update
// if you just want to replace the old good by the new good, you can use map
this.goods = this.goods.map(good => good.id === updatedGood.id ? updatedGood : good )
},
...
},
watch: {
// whenever question changes, this function will run
goodToUpdate: function (newGood, oldGood) {
if(newGood){
this.updateGood(newGood)
}
}
},
}

Is it possible to bind a class to an element based on a boolean expression?

I want to bind a class to an element based on the result of a boolean expression. For example:
<input type="email" :class="{ invalid: submitted && $v.email.$error }">
But the "invalid" class is not added to the element if I evaluate both conditions; it only works when I evaluate one or the other, for example:
<input type="email" :class="{ invalid: submitted }">
or
<input type="email" :class="{ invalid: $v.email.$error }">
work just fine. I realize I could use computed properties for this, but I'd have to create a computed property for each field in my web form and that seems redundant. Is there a better way?
<input type="email" :class="{ invalid: formValid($v.email.$error) }">
computed: {
formValid(){
return (val) {
val && this.submitted ? true : false
}
}
}
Could you test this out im curious if it would work, then you just have to pass a param and need one Computed.
Your Code
<input type="email" :class="{ invalid: submitted && $v.email.$error }">
The problem here, even though you defined $v in your local state, Vue is unable to find it. Try to define your local state in your data property without a preceding dollar sign. Because $ carries an extra meaning in Vue.
Generally $ means instance properties like data, el, root, children etc.
For example to access what element your Vue instance is mounted on, you can use this.$el.
So you can modify your code like this -
<input type="email" :class="{ invalid: submitted && $data.$v.email.$error }">
I think I've found a pretty good solution. I used a method with an argument instead of computed properties:
<template>
<form #submit.prevent="onSubmit" novalidate>
<input
type="email"
:class="{ invalid: isInvalid($v.email.$error) }"
v-model.lazy="email">
<button type="submit">Submit</button>
</form>
</template>
<script>
import { required, email } from 'vuelidate/lib/validators'
export default {
data () {
return {
email: '',
submitted: false
}
},
validations: {
email: {
required,
email
},
},
methods: {
isInvalid (val) {
return val && this.submitted
},
onSubmit () {
this.submitted = true
if (!this.$v.$invalid) {
// do something with the email address
}
}
}
}
</script>

Vuelidate check box validation

I am using Vue.js and I am new on it. I am currently working on validation. I had used vuelidate as my validation library. I had successfully done form validation, but trouble came when I had to check validation for check box.
How can I check validation for check box? Also, I had used bootstrapvue to display check box.
<b-col lg="6" md="6" sm="6">
<label>Bus Route</label>
<b-form-group>
<b-form-checkbox v-for="route in busRouteList"
v-model.trim="selectedRoute"
v-bind:value="route.value"
v-bind:unchecked-value="$v.selectedRoute.$touch()">
{{route.text}}</b-form-checkbox>
</b-form-group>
<div class="form-group__message" v-if="$v.selectedRoute.error && !$v.selectedRoute.required">
Field is required
</div>
</b-col>
validations: {
selectedRoute: {
required
},
}
As false is also valid value so, you should try to use sameAs
import { sameAs } from 'vuelidate/lib/validators'
terms: {
sameAs: sameAs( () => true )
}
You should bind #change methods:
<b-form-checkbox v-for="route in busRouteList"
v-model.trim="selectedRoute"
v-bind:value="route.value"
#change="$v.selectedRoute.$touch()">
and you might want to use custom function:
selectedRoute: {
checked (val) {
return val
}
},
This worked for me.
Basically, you need to make its value 'sameAs' a boolean 'true', which means the checkbox is checked.
So, i.e:
privacyCheck: {
sameAs: sameAs(true)
},
I hope this little example will help you to understand how to validate a checkbox.
You have to check when the input is changing. I recommend you to use #change.
In template
<div class="input">
<label for="country">Country</label>
<select id="country" v-model="country">
<option value="usa">USA</option>
<option value="india">India</option>
<option value="uk">UK</option>
<option value="germany">Germany</option>
</select>
</div>
<div class="input inline" :class="{invalid: $v.terms.$invalid}">
<input type="checkbox" id="terms" v-model="terms" #change="$v.terms.$touch()">
<label for="terms">Accept Terms of Use</label>
</div>
So the terms will be valid if selected country will be germany.
validations: {
terms: {
checked(val) {
return this.country === "germany" ? true : val;
}
}
}
of course country, terms are defined in data():
country:'',
terms: false
`
validations: {
terms: {
checked: (val) => {return val;}
}
}
`
With vuelidate-next (for both Vue 2 and Vue 3 support) it's so simple as using sameAs built-in validator with true as a direct parameter. For example, when using inside a setup method:
const termsAccepted = ref(false)
const v$ = useVuelidate(
{ termsAccepted: { sameAs: sameAs(true) } },
{ termsAccepted }
)
return { v$, termsAccepted }

Using Vee-validate to disable button until form is filled out correctly

I want to disable my submit button until my form is filled out correctly, this is what I have so far:
<form>
<input type="text" class="form-control" v-validate="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />
<span v-show="errors.has('email')">{{ errors.first('email') }}</span>
<button v-if="errors.any()" disabled="disabled" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
<button v-else="errors.any()" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
</form>
The above only prints an error message and disables my submit button after I've started inputting a value. I need it to be disabled from the start, before I start interacting with the input, so that I cannot send an empty string.
Another question is if there is a better way than using v-ifto do this?
EDIT:
userCreate: {
customerId: null,
userPrincipalName: '',
name: 'unknown',
isAdmin: false,
isGlobalAdmin: false,
parkIds: []
}
Probably simpliest way is to use ValidationObserver slot for a form. Like this:
<ValidationObserver v-slot="{ invalid }">
<form #submit.prevent="submit">
<InputWithValidation rules="required" v-model="first" :error-messages="errors" />
<InputWithValidation rules="required" v-model="second" :error-messages="errors" />
<v-btn :disabled="invalid">Submit</v-btn>
</form>
</ValidationObserver>
More info - Validation Observer
Setting up the button to be :disabled:"errors.any()" disables the button after validation. However, when the component first loads it will still be enabled.
Running this.$validator.validate() in the mounted() method, as #im_tsm suggests, causes the form to validate on startup and immediately show the error messages. That solution will cause the form to look pretty ugly. Also, the Object.keys(this.fields).some(key => this.fields[key].invalid); syntax is super ugly.
Instead, run the validator when the button is clicked, get the validity in the promise, and then use it in a conditional. With this solution, the form looks clean on startup but if they click the button it will show the errors and disable the button.
<button :disabled="errors.any()" v-on:click="sendInvite();">
Send Invite
</button>
sendInvite() {
this.$validator.validate().then(valid=> {
if (valid) {
...
}
})
}
Validator API
One way to disable a button until all the values you need are filled, is to use a computed property that will return bool if all values are assigned or not
Example:
Create a computed property like this:
computed: {
isComplete () {
return this.username && this.password && this.email;
}
}
And bind it to the html disabled attribute as:
<button :disabled='!isComplete'>Send Invite</button
This means, disable the button if !isComplete is true
Also, in your case you don't need two if/else-bound buttons. You can use just one to hide/show it based on if the form is completed or has any errors:
<button :disabled="errors.any() || !isCompleted" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
This button will be disabled until all fields are filled and no errors are found
Another way is to make use of v-validate.initial
<input type="text" class="form-control" v-validate.initial="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />
This will execute the validation of the email input element after the page is loaded. And makes that your button is disabled before interacting with the input.
To check whether a form is invalid or not we can add a computed property like this:
computed: {
isFormInValid() {
return Object.keys(this.fields).some(key => this.fields[key].invalid);
},
},
Now if you want to start checking immediately before user interaction with any of the fields, you can validate manually inside mounted lifecycle hooks:
mounted() {
this.$validator.validate();
}
or using computed
computed: {
formValidated() {
return Object.keys(this.fields).some(key => this.fields[key].validated) && Object.keys(this.fields).some(key => this.fields[key].valid);
}
}
and use
button :disabled="!formValidated" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">
For the current version 3 (As at the time of writing).
Step 1
Ensure form fields can be watched.
Step 2
Get a reference to the validator instance:
<ValidationObserver ref="validator">.
Step 3
Trigger validation silently whenever the form fields change.
Here's an example:
export default {
data() {
return {
form: {
isValid: false,
fields: {
name: '',
phone: '',
}
}
}
},
watch: {
'form.fields': {
deep: true,
handler: function() {
this.updateFormValidity();
}
}
},
methods: {
async updateFormValidity() {
this.form.isValid = await this.$refs.validator.validate({
silent: true // Validate silently and don't cause observer errors to be updated. We only need true/false. No side effects.
});
},
}
}
<button :disabled="form.isValid">
Submit
</button>
You can add computed properties
...
computed: {
isFormValid () {
return Object.values(this.fields).every(({valid}) => valid)
}
}
...
and it bind to the button:
<button :disabled="!isFormValid" class="btn btn-primary" type="submit">Send Invite</button>
i try this on vee-validate version ^2.0.3