Vee validate get previous value after vue set - javascript

I want to update v-model value by code and validate that field. So I am using vue.set method for updating value and then call $validator.validate.
my code is something like that.
Vue.set(model,property, value);
vm.$validator.validate(property).then(function (valid) {
if (!valid) {
vm.$validator.flag(property, {
touched: true,
dirty: true
});
}
});
my validation rules code is somethng like that:
Validator.extend("val_compulsory", {
getMessage(field, args) {
return args[0];
},
validate(value) {
return {
valid: !!value,
data: {
required: true
}
};
}
}, { computesRequired: true });
but in val_compulsory validator I always get previous value which is before vue.set. Is there any way to get latest value in vee-validator validation methods after vue.set?

Try this:
Vue.set(model,property, value);
vm.$nextTick(function() {
vm.$validator.validate(property).then(function (valid) {
if (!valid) {
vm.$validator.flag(property, {
touched: true,
dirty: true
});
}
});
});

Related

Vue.js 2: return statement returning multiple values in parentheses (watch & computed)

EDITED: Added more code for context as requested by the community.
I have a Vue.js 2 app that I am now maintaining where there is a computed property. This computed property has a return statement that is returning several data properties in a comma-separated format, and the entire group is wrapped in parentheses.
I tried looking at both Javascript and Vue.js 2 documentation, and I can't figure out what this is doing. In Javascript, if you wanted to return multiple values, you would return an array with the values or an object with multiple properties.
Here is the other weird thing. This "compoundFormProperty" computed property isn't used in the component template! There is a watcher on this "compoundFormProperty" property. I searched the entire code base, and "compoundFormProperty" only exists in this one component. Since it's not used by the component template, I think it is being used to track some internal state (I think it's used to check if the form is valid). The rest of the computed properties have get() and set() calls, so they appear to reference a Vuex store that stores the data.
<script>
import { mapState } from "vuex";
export default {
name: "DeliveryAddress",
components: {},
props: {
countries: Array,
usStates: Array,
canStates: Array,
errorsCount: Number,
isEdit: Boolean,
selectedAddress: Boolean,
sameAsBilling: Boolean
},
data: function() {
return {
sameAddress: false,
saveAddress: false,
nameEmpty: true,
nameTouched: false,
addressEmpty: true,
addressTouched: false,
cityEmpty: true,
cityTouched: false,
postalTouched: false,
invalidDeliveryPostal: true,
invalidBillingPostal: true,
formTouched: false,
storedInfo: {},
cancel: false,
editInvalid: false,
saveInProgress: false,
sameBilling: this.sameAsBilling
};
},
computed: {
formInvalid: {
get() {
if (this.selectedAddress) {
return false;
}
return (
this.formTouched === false ||
this.nameEmpty ||
this.cityEmpty ||
this.addressEmpty ||
this.invalidDeliveryPostal
);
}
},
compoundFormProperty: function() {
return (
this.deliveryName,
this.deliveryCity,
this.deliveryAddress,
this.deliveryState,
this.deliveryCountry,
this.deliveryPostal,
Date.now()
);
},
...mapState([
"name",
"city",
"address",
"addressTwo",
"country",
"state",
"postal",
"cartObj",
"accountObj"
]),
cartObj: {
get() {
return this.$store.state.cartObj;
},
set(value) {
this.$store.commit("setCart", value);
}
},
deliveryName: {
get() {
return this.$store.state.deliveryName;
},
set(value) {
this.$store.commit("setDeliveryName", value);
}
},
deliveryAddress: {
get() {
return this.$store.state.deliveryAddress;
},
set(value) {
this.$store.commit("setDeliveryAddress", value);
}
},
deliveryAddressTwo: {
get() {
return this.$store.state.deliveryAddressTwo;
},
set(value) {
this.$store.commit("setDeliveryAddressTwo", value);
}
},
deliveryCity: {
get() {
return this.$store.state.deliveryCity;
},
set(value) {
this.$store.commit("setDeliveryCity", value);
}
},
deliveryState: {
get() {
return this.$store.state.deliveryState;
},
set(value) {
this.$store.commit("setDeliveryState", value);
}
},
deliveryCountry: {
get() {
return this.$store.state.deliveryCountry;
},
set(value) {
this.$store.commit("setDeliveryCountry", value);
}
},
deliveryPostal: {
get() {
return this.$store.state.deliveryPostal;
},
set(value) {
this.$store.commit("setDeliveryPostal", value);
}
},
accountObj: {
get() {
return this.$store.state.accountObj;
},
set(value) {
this.$store.commit("setAccount", value);
}
}
},
watch: {
compoundFormProperty() {
if (this.sameBilling || this.selectedAddress) {
this.$emit("change", { errors: false });
} else if (
this.deliveryName &&
this.deliveryCity &&
this.deliveryAddress &&
this.deliveryState &&
this.deliveryCountry &&
this.invalidDeliveryPostal === false
) {
this.$emit("change", { errors: false });
} else if (
this.isEdit &&
this.deliveryName &&
this.deliveryCity &&
this.deliveryAddress &&
/^[-/\w ]{2,12}$/.test(this.deliveryPostal)
) {
this.editInvalid = false;
} else {
this.$emit("change", { errors: true });
this.editInvalid = true;
}
}
//Other watchers
}
}
</script>
JavaScript functions can't return multiple values. Expressions delimited by comma operators are evaluated to the last one, i.e. compoundFormProperty returns Date.now().
It could be a mistake with comma-separated expression being returned instead of an array. But here it looks like a hack to trigger reactivity in computed property.
Due to how Vue reactivity works, deliveryName, etc. properties are marked to be tracked when accessed on this. compoundFormProperty will run on any changes in instance properties that are accessed inside computed property.
The explicit and not hacky way is to rewrite this as a collection watcher:
data() {
return { compoundFormProperty: null, ... }
},
created() {
this.$watch(
['deliveryName', ...],
() => {
this.compoundFormProperty = Date.now()
},
{ immediate: true }
);
}
The result may be somewhat different depending on the use, a watcher with immediate causes compoundFormProperty value to be assigned immediately, while computed property is evaluated the first time when it's accessed.
UPD: the combination of compoundFormProperty computed property and watcher in original post is a very hacky way to write this.$watch with array input, likely because a developer wasn't aware of it, or the code derived from Vue 1.x that didn't have specific functionality to shallowly watch collections. Date.now() value is efficiently ignored and used to not cache a computed.

How can use computed for v-model?

I got data from API using computed. "UserModule.userInfo.usrEmail" is the state in my vuex. Like below:
data() {
return {
vModel: {
email: {
value: "",
},
}
}
}
computed: {
email:{
get: function(){
return UserModule.userInfo ? UserModule.userInfo.usrEmail : "";
},
set : function(email){
this.vModel.email.value = email
}
},
}
And then show it to user like below:
<input v-model="email"></input>
User can edit email and also cancel their edit process and return to their previous data but in edit everything was correct but when i want to cancel this process my previous data did not show in the input and i saw my new data which is not correct i want to cancel it. This is my cancel method on input:
resetInput(input) {
this.vModel.email.value = this.email
},
"this.email" refer to my computed which is get data from API.
How can i write this cancel process correctly and see my previous data in input tag?
so you can use this solution:
data() {
return {
useGet :{
email: true,
},
}
}
in your method:
resetInput(input) {
this.useGet.email = true
},
and in your computed:
email: {
get: function () {
if (this.useGet.email) {
return UserModule.userInfo ? UserModule.userInfo.usrEmail : ""
}
return ""
},
set: function (email) {
this.useGet.email = false
}
},
this is because of your UserModule.userInfo.usrEmail.
this state does not update.
your get in computed will be work when your UserModule.userInfo.usrEmail changes.

Best way to assign property to javascript property

I am using vueJs computed to create options for my component like this:
computed:{
fileOptions() {
let fileOptions = [
{
event:'event',
name:'Abcd',
disabled://based upon some condition,
display://based upon some condition
},
{
event://based upon some condition,
name:'Open Presentation',
disabled://based upon some condition,
display://based upon some condition
},
]
}
}
The event, disabled and display property are based upon multiple conditions.
One way of doing this is by using ternary operator
disabled:this.state.libraryActive=='presentations'?false:true
However, it is easy for one condition but for multiple conditions it becomes difficult.
Any suggestions?
The best practice is to use another computed property:
computed:{
fileOptions() {
let fileOptions = [
{
event:'event',
name:'Abcd',
disabled: this.isAbcdDisabled, // based upon some condition,
display: this.isAbcdVisible // based upon some condition,
},
{
event: this.getEventName, // based upon some condition,
name:'Open Presentation',
disabled: this.getDisabled(this.getEventName), // based upon some condition,
display: this.getVisible(this.getEventName) //based upon some condition
},
]
},
isAbcdDisabled ()
{
return this.state.libraryActive === 'presentations' && !this.admin ? false : true
},
isAbcdVisible ()
{
return true;
},
getEventName ()
{
return this.canEdit ? 'edit' : 'add';
}
},
methods:
{
getDisabled (eventName)
{
switch(eventName)
{
case 'edit': return false;
case 'add': return true;
default: return false;
}
},
getVisible (eventName)
{
switch(eventName)
{
case 'edit': return true;
case 'add': return true;
default: return false;
}
},
}
You don't need to use ternary operator, you could make it much easier:
disabled: this.state.libraryActive !== 'presentations'
It will return false if this.state.libraryActive is equal to 'presentations' and true otherwise.

express-validator: skip further validation in custom validator

I am using express-validator to validate my request data. According to the docs to add them we have to declare them like this
app.use(expressValidator({
customValidators: {
isArray: function(value) {
return Array.isArray(value);
},
gte: function(param, num) {
return param >= num;
}
}
}));
I added a validator to it like the docs. but I can only return true or false.
I want to make sure that based on the condition validator skip further chain validation.
You could do it in stages. Here I first check whether username is set, and only if that passes do I then do an expensive async validation.
req.checkBody('username')
.notEmpty().withMessage('Username is required.');
req.getValidationResult().then(result => {
if (!result.isEmpty()) {
return res.status(400).send(result.mapped());
}
req.checkBody('username')
.isUserNameAvailable().withMessage('Username is not available.');
req.getValidationResult().then(result => {
if (!result.isEmpty()) {
return res.status(400).send(result.mapped());
}
next();
});
})

Javascript Validate URL

I use the following code to validate URL in client side. My question is if there is a better way to do the same..My question is not about the regex that i am using (just the overall way of writing)
this.validators.myUrl = function(value) {
if (!_.isUndefined(value) && !_.isNull(value)) {
if ($.trim(value).length == 0) {
return {
isValid: true
}
} else {
return /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/i.test($.trim(value)) == true ? {
isValid: true
} : {
isValid: false,
message: "Enter a valid URL"
};
}
} else {
if ($.trim(value).length == 0) {
return {
isValid: true
}
}
}
};
Why you don't want to use ready plugins/libraries?
You can have used jQuery Validation Plugin and its url method.
All what you need is:
$( "#myform" ).validate({
rules: {
field: {
required: true,
url: true
}
}
});
where myform is id of your form.

Categories