Vee validate: isBetween custom rule with parameters not working - vue.js

validate, and Im trying to create multiple rules for my textfield for example: required, minlength, maxLength and chain them together, and based on whic h parameter is passed to preform validation
So I tried using example from the docs:
http://vee-validate.logaretm.com/v2/guide/custom-rules.html#args-and-rule-configuration
const isBetween = (value, { min, max } = {}) => {
return Number(min) <= value && Number(max) >= value;
};
// The first param is called 'min', and the second is called 'max'.
const paramNames = ['min', 'max'];
Validator.extend('between', isBetween, {
paramNames // pass it in the extend options.
});
And my Vue model looks like this:
<ValidationProvider
v-if="item && item.type === 'TEXT_AREA'"
:rules="`isBetween:true, 10`"
v-slot="{ errors, valid, validate }"
>
<b-form-textarea
size="sm"
:id="`attribute`"
:value="attributeValue"
#input="addAttributeValue($event, uid, validate)"
/>
<span>{{ displayError(errors) }}</span>
</ValidationProvider>
Here I try to pass in IsBeterrn params like: required, length and based on that to preform validation but I always get min & max value as null, and arguments is array instead of object
Also my second question is how would I use required from vee-validate in my custom rule

You have two ways of specifying parameters, with strings or with objects. I suggest you use the object method like this:
<ValidationProvider
:rules="{between:[0, 10]}"
>
You had a couple mistakes - the rule is called between because that's what you called it when you did this:
Validator.extend('between', isBetween, {
paramNames // pass it in the extend options.
});
Also, you can't use a boolean and a number as the parameter as you did here:
:rules="`isBetween:true, 10`"
The way I specified it, with :rules="{between:[0, 10]}" also lets you make the min and max variable if you wanted, i.e. if you had a component data item called minValue you could use that in the rule like this :rules="{between:[minValue, 10]}" and your rules would react to changes to minValue.

Related

Trying to block user from typing numbers on an antd Input without having to use state (or useState)

I'm using antd Form and have some Inputs in it. I wish to know if antd has something that can validate the users input and determine if what he typed are letters or numbers. I want the input to only allow the user to type (show) in the input only alphabets but not numbers.
Form.Item has a property called rules where you can establish a pattern using RegExp, but this is not what I want, because the user can still type numbers and they will be shown on the UI. Pattern property only pops a message like "field does not accept numbers" and I assume that when I submit the data it will take what the user typed regardless of the failed rule/pattern.
I have been able to do what I'm asking by having to create a state with an object with all my values, removing the name property from the Form.Item and adding it to the Input, along with an onChange and value property but this requires more "wiring'. If you have worked with antd Form, there is an onFinish property that collects all the data so you don't have to create a separate state.When removing the name property from Form.Item, antd won't be able to collect the data of the form anymore thus rendering it useless. I wish to know if antd has something that can help me achieve what I typed in the beginning.
Following code does what I want, but uses state.
My Form.Item
<Form.Item
// name="nombre"
label="Nombre(s)"
rules={[
{ required: false, message: "REQUIRED_ERROR_MESSAGE" },
{
pattern: new RegExp(/^[a-záéíóúüñçA-Z]*$/i),
message: "field does not accept numbers",
},
]}
>
<Input
onChange={handleWordInput}
value={altaFormValues.nombre}
name="nombre"
/>
</Form.Item>
My Input handler function:
const handleWordInput = (e) => {
console.log("e", e.target.value);
const re = /^[a-záéíóúüñçA-Z\s]*$/;
const { name, value } = e.target;
if (value === "" || re.test(value)) {
setAltaFormValues((prevState) => {
return { ...prevState, [name]: value };
});
}
};

vee validate 2.2 - Custom rule with two cross-field validation parameters

I am trying to validate a field with vee validate that depends on 2 other input parameters.
The field which needs to be validated is an average value field that must not be greater than a given maximum and not less than a given minimum.
<v-text-field v-model="kind.bhd_min"
v-validate="'required|minBHD:maxBHD'"
v-currency="{locale:'de', currency:null}"
ref="minBHD"
...>
</v-text-field>
<v-text-field v-model="kind.bhd_max"
v-validate="'required'"
v-currency="{locale:'de', currency:null}"
ref="maxBHD"
...>
</v-text-field>
<v-text-field v-model="kind.average_bhd"
:data-vv-name="`average_bhd-${index}`"
v-validate="'required|averageBHD:minBHD,maxBHD'"
v-currency="{locale:'de', currency:null}"
:error-messages="errors.first(`average_bhd-${index}`)">
</v-text-field>
My custom rule looks like this:
const averageBHD = {
validate: (value, [min,max] = {}) => {
console.log(value,min,max);
return value > min && value < max;
},
};
Validator.extend('averageBHD', averageBHD, {hasTarget: true});
The custom rule is called properly as the console.log() outputs some values when dirtying the related fields. However, the second parameter (max) is never the actual value of the the referenced field but the string literal "maxBHD" which appears to be the value of the ref attribute. The first parameter min is logged correctly, thus indicating that the ref is working.
What am I doing wrong? It should be possible to reference multiple fields in a custom rule, right?

vuetify rule function - how to access component label during validation?

I would like to access the label property of a component in it's "Rules" function so I can return an (already) localized field name in the error message.
Is there any way to access the properties of the component in the rule function that's called by Vuetify for validation?
<v-text-field
v-model="obj.count"
:counter="10"
:label="this.$locale.get('WidgetCount')"
:rules="MyRuleFunctionInMyRuleLibrary()"
name="count"
required
></v-text-field>
As can be seen in the code I have a function to localize the field label already, I don't want to re-do it twice or have to specify it twice. In "MyRuleFuctionInMyRuleLibrary" I want to validate the rule and report on it localized properly.
I know I can just pass the localized text Key in my rule function but that would create a redundancy as I would have to type it twice in the template and I also need some other properties of the control / component so I would rather pass or have access to the component itself. I already tried passing "this" to the component, e.g.:
:rules="MyRuleFunctionInMyRuleLibrary(this, obj.count)"
However this in this case appears to be everything on the page / form, not the single component itself.
Using typescript:
<v-text-field v-model="volume.sizePerInstance" :rules="sizePerInstanceRules" :label="$t('volumes.sizePerInstance') + ' (GB)'" type="number" step="0.01" required min="0" color="#0cc2aa"></v-text-field>
You have to define a getter in order to get acces to component properties:
get sizePerInstanceRules() {
return [
(v: number) => v && v > 0 || 'Max size must be greater than 0',
(v: any) => v && !isNaN(v) || 'Max size must be a number',
(v: number) => {
return this.maxValue >= v || 'Exceeded limit';
},
];
}
In Vuetify source code, rules function has only 1 parameter (value). You can work around by define label as data or computed property:
<v-text-field
v-model="obj.count"
:counter="10"
:label="label.count"
:rules="MyRuleFunctionInMyRuleLibrary()"
name="count"
required
></v-text-field>
Add label to data
data: () => ({
label: {
count: this.$locale.get('WidgetCount')
}
})
then you can access localize label in validation function by this.label.count
You might want to watch locale change to change label manually:
watch: {
locale: function () {
this.label = {
count: this.$locale.get('WidgetCount')
}
}
}

React-Native + Redux: Random number of form fields

I am a newbie to react-native, redux and saga and have run into a use case that I have not been able to find a solution for. I understand how to map state to properties and pass around the state between action, reducer and saga. This makes sense to me so far. This is where things seem to get dicey. I have a form that requires a variable number of form fields at any given time depending upon what is returned from the database.
As an example, let's say I have a structure like this:
{
name: ‘’,
vehicleMake: ‘’,
vehicleModel: ‘’,
carLotCity: ‘’,
carLotState: ‘’,
carLotZipCode: ‘’,
localPartsManufacturers: [{name: ‘’, address: ‘’, zipCode}]
}
Everything from name to carLotZipCode would only require one text field, however, the localPartsManufacturers array could represent any number of object that each would need their own set of text fields per each object. How would I account for this with redux as far as mapping the fields to the state and mapping the state to the properties? I am confused about how to begin with this scenario. I understand how to project mapping when the fields are fixed.
I would keep the data as it is coming from the backend. That way you'll avoid normalizing it. I think we just have to be smarter when rendering the fields. Here's what I'm suggesting:
function onTextFieldChange(name, index) {
// either name = `name`, `vehicleMake`, ...
// or
// name = `localPartsManufacturers` and `index` = 0
}
function createTextField(name, index) {
return <input
type='text'
name={ name }
onChange={ () => onTextFieldChange(name, index) } />;
}
function Form({ fields }) {
return (
<div>
{
Object.keys(fields).reduce((allFields, fieldName) => {
const field = fields[fieldName];
if (Array.isArray(field)) {
allFields = allFields.concat(field.map(createTextField));
} else {
allFields.push(createTextField(fieldName));
}
return allFields;
}, [])
}
</div>
);
}
Form receives all the data as you have it in the store. Then we check if the field is an array. If it is an array we loop over the fields inside and generate inputs same as the other properties createTextField. The tricky part here is how to update the data in the store. Notice that we are passing an index when the text field data is changed. In the reducer we have to write something like:
case FIELD_UPDATED:
const { name, index, value } = event.payload;
if (typeof index !== 'undefined') {
state[name][index] = value;
} else {
state[name] = value;
}
return state;
There is nothing preventing you from keeping a list, map, set or any other object in Redux.
The only thing remaining then, is how you map the state to your props, and how you use them. Instead of mapping a single element from the collection to a prop, you map the entire collection to a single prop, and then iterate over the collection in your render method.
In the action you can pass a new collection back, which is comprised of the form fields making up the parts list. Then, your reducer will replace the collection itself.
Or, upon changing an element in the part collection, you can send an action with its id, find it in the collection in the reducer and replace the element that was changed / add the new one / remove the deleted one.

Dynamically changing jQuery unobtrusive validation attributes

I have a page built in ASP.NET MVC 4 that uses the jquery.validate.unobtrusive library for client side validation. There is an input that needs to be within a range of numbers. However, this range can change dynamically based on user interactions with other parts of the form.
The defaults validate just fine, however after updating the data-rule-range attribute, the validation and message are still triggered on the original values.
Here is the input on initial page load:
<input id="amount" data-rule-range="[1,350]" data-msg-range="Please enter an amount between ${0} and ${1}">
This validates correctly with the message Please enter an amount between $1 and $350 if a number greater than 350 is entered.
After an event fires elsewhere, the data-rule-range is updated and the element looks as such:
<input id="amount" data-rule-range="[1,600]" data-msg-range="Please enter an amount between ${0} and ${1}">
At this point if 500 is entered into the input it will fail validation with the same previous message stating it must be between $1 and $350. I have also tried removing the validator and unobtrusiveValidation from the form and parsing it again with no luck.
$('form').removeData('validator');
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
Is there a clean way to change the validation behavior based on the input attributes dynamically?
As Sparky pointed out changing default attributes dynamically will not be picked up after the validation plugin has been initialized. To best work around this without rewiring how we register validated fields and rules, I found it easiest to register a custom adapter in the unobtrusive library:
jQuery.validator.unobtrusive.adapters.add("amount", {}, function (options) {
options.rules["amount"] = true;
options.messages["amount"] = function () { return $(options.element).attr('data-val-amount'); };
});
jQuery.validator.addMethod("amount", function (val, el, params) {
try {
var max = $(el).attr('data-amount-max');
var min = $(el).attr('data-amount-min');
return val <= max && val >= min;
} catch (e) {
console.log("Attribute data-amount-max or data-amount-min missing from input");
return false;
}
});
Because the message is a function, it will be evaluated every time and always pick up the latest attribute value for data-val-amount. The downside to this solution is that everytime there is a change we need to change all three attributes on the input: data-amount-min, data-amount-max, and data-val-amount.
Finally here is the input markup on initial load. The only attribute that needs to be present on load is data-val-amount.
<input id="amount" data-val-amount="Please enter an amount between ${0} and ${1}" data-val="true">
You cannot change rules dynamically by changing the data attributes. That's because the jQuery Validate plugin is initialized with the existing attributes on page load... there is no way for the plugin to auto re-initialize after dynamic changes are made to the attributes.
You must use the .rules('add') and .rules('remove') methods provided by the developer.
See: http://jqueryvalidation.org/rules/
you can try this one:
// reset the form's validator
$("form").removeData("validator");
// change the range
$("#amount").attr("data-rule-range", "[1,600]");
// reapply the form's validator
$.validator.unobtrusive.parse(document);
charle's solution works! you cannot have model attributes to use it though, I build my inputs like:
#Html.TextBoxFor(m => Model.EnterValue, new
{
#class = "form-control",
id="xxxx"
data_val = "true",
data_val_range = String.Format(Messages.ValueTooBig, Model.ParamName),
data_val_range_max = 6,
data_val_range_min = 2,
data_val_regex_pattern = "^\\d+(,\\d{1,2})?$"
})
and then in javascript:
$("form").removeData("validator");
$("#xxxx").attr('data-val-range-max', 3)
$("#xxxx").attr('data-val-range-min', 0)
$.validator.unobtrusive.parse(document);