Vee-validate extend validation with 3rd party library - vee-validate

I tried to validate a phone number field using vee-validate and awesome-phonenumber but even I enter a correct value, error from validation still showing up. Also did check the value from console but still error message is thrown.
import { extend } from 'vee-validate'
import PhoneNumber from 'awesome-phonenumber'
extend('phone_number', {
message () {
return 'This field is not a valid phone number'
},
validate (value) {
let phone = new PhoneNumber(value)
if(phone.isValid() && phone.getRegionCode() === 'AU') {
console.log(phone.getRegionCode()) // this is true but error message still showing
} else {
console.log('not valid')
}
}
})

You need to return true in your vee-validate rules, I don't see you returning anything which means undefined and vee-validate treats undefined the same as `false.
import { extend } from 'vee-validate'
import PhoneNumber from 'awesome-phonenumber'
extend('phone_number', {
message () {
return 'This field is not a valid phone number'
},
validate (value) {
let phone = new PhoneNumber(value)
if(phone.isValid() && phone.getRegionCode() === 'AU') {
return true;
}
return false;
}
})

Related

Adding custom validation on checkout field country

I need to add a validation on the country field in all place where it can be used.
For registration or address editing it work fine but in checkout I tried several method but nothing worked. I want the validation on the field of the new shipping address form.
I add my validation in an js file countryValidation.js :
Same script for registration or edit address and it work fine
define([
'jquery',
'jquery/ui',
'mage/validation',
'mage/translate',
'domReady!'
], function($){
'use strict';
return function(validator) {
$.validator.addMethod(
"validate-country",
function(value, element) {
if (value === "FR") {
var zipValue = $('input[name="postcode"]').val();
if (zipValue) {
return !(zipValue.startsWith("97") || zipValue.startsWith("98"));
}
}
return true;
},
$.mage.__("You cannot choose France for DOM-TOM Zip Code")
);
return validator;
}
});
I registered it in requirejs-config.js in my module :
var config = {
config: {
mixins: {
'Magento_Ui/js/lib/validation/validator': {
'Gone_Customer/js/countryValidation': true
}
}
}
};
For adding validation to checkout method I tried different method
-> Method A : With a plugin
class AddCountryValidation
{
public function afterProcess(
\Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
array $jsLayout
) {
// Country ID
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children']['country_id']['validation']['validate-country'] = true;
return $jsLayout;
}
}
-> Method B : add validation rule in attribute
In customer_eav_attribute in attribute country_id for validate_rules I added {"validate-country": true}
When I validate the form I have no validation error when I should have one.
Can you tell me if I'm missing something please?
Thank you for your response in the meantime I found something that worked.
I had to do a seperate js validation only for checkout :
define(['mage/translate', "jquery"], function($t, $) {
'use strict';
return function(rules) {
rules['validate-country'] = {
handler: function (value) {
if (value === "FR") {
var zipValue = $('input[name="postcode"]').val();
if (zipValue) {
return !(zipValue.startsWith("97") || zipValue.startsWith("98"));
}
}
return true;
},
message: $t('You cannot choose France for DOM-TOM Zip Code')
};
return rules;
};
});
And in my mixin I change Magento_Ui/js/lib/validation/validator for Magento_Ui/js/lib/validation/rules then my plugin method was working !
Please try this
define([
'jquery',
'jquery/validate'
], function($){
'use strict';
return function(validator) {
validator.addRule(
"validate-country",
function(value) {
if (value === "FR") {
var zipValue = $('input[name="postcode"]').val();
if (zipValue) {
return !(zipValue.startsWith("97") || zipValue.startsWith("98"));
}
}
return false;
},
$.mage.__("You cannot choose France for DOM-TOM Zip Code")
);
return validator;
}
});
I have used return false you can modify according to your need.Tested on version 2.2.2

Angular2 Async Form Validator (return Promise)

I'm trying to update the Angular2 Forms Validation example to handle an Async Validation response. This way I can hit an HTTP endpoint to validate a username.
Looking at their code they currently aren't currently using a Promise and it's working just fine:
/** A hero's name can't match the given regular expression */
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
const name = control.value;
const no = nameRe.test(name);
return no ? {'forbiddenName': {name}} : null;
};
}
I'm trying to update to return a Promise. Something like:
/** A hero's name can't match the given regular expression */
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl) => {
const name = control.value;
return new Promise( resolve => {
resolve({'forbiddenName': {name}});
});
};
}
However, the result I get doesn't display the error message, it's showing undefined.
My thought is it has something to do with the way they are handling displaying the errors:
onValueChanged(data?: any) {
if (!this.heroForm) { return; }
const form = this.heroForm;
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
However I'm not sure of a better way of doing this.
Angular2 example:
https://angular.io/docs/ts/latest/cookbook/form-validation.html#!#live-example
Link to my example attempting to return Promise:
https://plnkr.co/edit/sDs9pNQ1Bs2knp6tasgI?p=preview
The problem is that you add the AsyncValidator to the SyncValidator Array. AsyncValidators are added in a separate array after the SyncValidators:
this.heroForm = this.fb.group({
'name': [this.hero.name, [
Validators.required,
Validators.minLength(4),
Validators.maxLength(24)
],
[forbiddenNameValidator(/bob/i)] // << separate array
],
'alterEgo': [this.hero.alterEgo],
'power': [this.hero.power, Validators.required]
});

ng2: reserve Original value if validation failed

I am trying to force the user to fill in the description when they update an item. It validates, shows error message when validation fails, stops running execution and doesn't update an item.
Please see the series of screenshot below:
However, my item is still updated even if the validation fails. It seems to me that since an object is reference in the memory, it's still updated even if it doesn't run updateTodo() method from the Todoservice.
Is it because I am just hardcoding my items just for the testing? I am very new to Angular and I don't want to implement webAPIs at this point yet.
I tried to use Object.assign({}, copy) in getTodoItem(id: number) to clone and decouple my todoItem from the list but the error message showing that it's not observable.
How can I preserve the values of Objects in the list if the validation fails? In real life application, Since we retrieve the data from the database (or webapi cache) whenever index/list component is navigated, this problem shouldn't occur. Is my assumption right?
todoService.ts
import { Itodo } from './todo'
const TodoItems: Itodo[] = [
{ todoId: 11, description: 'Silencer' },
{ todoId: 12, description: 'Centaur Warrunner' },
{ todoId: 13, description: 'Lycanthrope' },
{ todoId: 14, description: 'Sniper' },
{ todoId: 15, description: 'Lone Druid' }
]
#Injectable()
export class TodoService {
getTodoItems(): Observable<Itodo[]> {
return Observable.of(TodoItems);
}
getTodoItem(id: number): Observable<Itodo> {
return this.getTodoItems()
.map((items: Itodo[]) => items.find(p => p.todoId === id));
//let copy = this.getTodoItems()
// .map((items: Itodo[]) => items.find(p => p.todoId === id));
//return Object.assign({}, copy);
}
addNewTodo(model: Itodo): number {
return TodoItems.push(model); // return new length of an array
}
updateTodo(model: Itodo) : number {
let idx = TodoItems.indexOf(TodoItems.filter(f => f.todoId == model.todoId)[0]);
return TodoItems.splice(idx, 1, model).length; // return the count of affected item
}
}
todo-edit.component.ts -- EditItem() is the main
import { Subscription } from 'rxjs/Subscription';
import { Itodo } from './todo'
import { TodoService } from './todo.service';
#Component({
moduleId: module.id,
templateUrl: "todo-edit.component.html"
})
export class TodoEditComponent implements OnInit, OnDestroy {
todoModel: Itodo;
private sub: Subscription;
Message: string;
MessageType: number;
constructor(private _todoService: TodoService,
private _route: ActivatedRoute,
private _router: Router) {
}
ngOnInit(): void {
this.sub = this._route.params.subscribe(
params => {
let id = +params['id'];
this.getItem(id);
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}
getItem(id: number) {
this._todoService.getTodoItem(id).subscribe(
item => this.todoModel = item,
error => this.Message = <any>error);
}
EditItem(): void {
this.todoModel.description = this.todoModel.description.trim();
if (!this.todoModel.description) {
this.Message = "Description must not be blank.";
this.MessageType = 2;
return;
}
console.log('valid: update now.');
let result = this._todoService.updateTodo(this.todoModel);
if (result > 0) {
this.Message = "An Item has been updated";
this.MessageType = 1;
}
else {
this.Message = "Error occured! Try again.";
this.MessageType = 2;
}
}
}
Working Solution
Object.assign it's the right method to use. I was using it wrongly in the service to clone it. You need to use it in your component, not in the service.
getItem(id: number) {
//Object.assign clone and decouple todoModel from the ArrayList
this._todoService.getTodoItem(id).subscribe(
item => this.todoModel = Object.assign({}, item),
error => this.Message = <any>error);
}
Validation does not prevent updating items, it just checks actual values for validity. You should create copy of object for editing to be able to rollback changes. You can use Object.assign({}, item) or JSON.parse(JSON.stringify(...)).

react-redux action to reducer relationship

So I am learning React-Redux. I am trying to find a way to load a value from the database first on load instead of just starting from 0. I have written an action that will hit an endpoint on my node file and pull from my mongo database. This works. However the action never seems to reach the reducer to actually update the store. Can someone explain to me the right way to make sure this action is store aware.
Here is the code for the action. Note the console.log with the number prints out what I want. I just never see the logs in the reducer that it was even ever reached.
export function setFooClicks(){
console.log("in the set foo clicks action")
var number = 0;
//return function(dispatch){
//console.log("in the return function")
return axios.get('/clicks').then(result => {
number = result.data
console.log("The number of clicks is", number)
//return number
return{
type: types.SETFOOCLICKS,
totalFoo: result.data
}
}).catch((error) => {
return console.log(error);
})
//}
}
I am trying to grab it in the top level container at the moment so here is the code for that.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Foo from '../components/Foo'
import { incrementFoo, setFooClicks } from '../actions'
class FooContainer extends Component {
componentDidMount(){
setFooClicks();
}
render() {
return (
<div>
<Foo incrementFooAction={() => this.props.incrementFoo()} totalFoo={this.props.totalFoo}/>
</div>
)
}
}
function mapStateToProps(state) {
return {
totalFoo: state.foo.totalFoo
}
}
export default connect(mapStateToProps, { incrementFoo,setFooClicks })(FooContainer)
The normal incrementFoo action works but trying to add the setFooClicks action into the component as well doesn't. Lastly here is the code to the reducer funciton. All I did was add a case to the switch block.
export default function incrementFoo(state = initialState, action) {
console.log("I am in the foo reducer")
console.log(action.type)
switch (action.type) {
case INCREMENT:
console.log(state.totalFoo);
return {
...state,
totalFoo: state.totalFoo + 1
}
case SETFOOCLICKS:
console.log("in the SETFOOCLICKS reducer")
return{
...state,
totalFoo: action.totalFoo
}
default:
return state
}
}

How do you make a kendo datepicker do date validation for a minimum date?

I have the following control:
#(Html.Kendo().DatePickerFor(model => model.Attributes.DueDate)
.HtmlAttributes(new {
ID = "idSurvey_DueDate",
#data_bind = "value: DueDate",
#Class = "report-label datepicker surveyAttributesData",
TabIndex = 3 })
.Min(DateTime.Now)
)
And the following jquery:
$("#idSurvey_DueDate").kendoValidator({
rules: {
dateValidation: function (e) {
var currentDate = kendo.parseDate($(e).val());
// Check if date parse was successful
if (!currentDate) {
return false;
}
return true;
}
},
messages: {
dateValidation: "Invalid Date!",
min: "Date must not be in the past!"
}
});
When I test this out and enter in an invalid date the message I get isn't what I expect. Instead it is "The field DueDate must be a date." Where is this mystery message coming from and why isn't it using the messages property I put in the validator? All I want is for non-valid date formats to not be allowed and for the date to not be in the past. So a minimum must be enforced.
This code seems to work fine:
$("form").kendoValidator({
rules: {
dateValidation: function(element) {
var value = $(element).val();
var date = kendo.parseDate(value);
if (!date) {
return false;
}
return true;
},
minDate: function(element) {
var value = $(element).val();
var date = kendo.parseDate(value);
var result = date >= new Date();
return result;
}
},
messages: {
dateValidation: "You must enter a date",
minDate: "The date must not be in the past"
}
});
Here is a live demo: http://jsbin.com/EvoroRe/1/edit
I suggest to add the mvcdate rule:
rules: {
mvcdate: function (input) {
var datarole = $(input).data('role');
if (datarole === 'datepicker') {
var value = $(input).val();
if (value) {
var date = kendo.parseDate(value, 'ddd, MMM d');
if (!date) {
return false;
}
}
}
return true;
}
},
messages: {
mvcdate: function (intput) {
return intput.attr('data-val-date');
}
}
Unfortunatelly dateValidation rule has a lower priority that date and mvcdate just because they are default and nor custom one. As I have understood the mvcdate rule has the highest priority because:
dateValidation rule has been skipped for the certain control and I got the 'must be a date' error
date rule has been passed with the TRUE result but I still got the 'must be a date' error
mvcdate rule has helped me alone.
You always can look to the kendoValidator in the console:
I'm not sure if the kendo validator changed since the accepted answer, but you'll want to filter out and only apply date validation to datepicker inputs. Otherwise a textbox or other input will generate an error message about an invalid date. The rules should look like
$("#modForm").kendoValidator({
rules: {
dateValidation: function (input) {
if (input.is('[data-role="datepicker"]')) {
var value = $(input).val();
var date = kendo.parseDate(value);
if (!date) {
return false;
}
}
return true;
}
},
messages: {
dateValidation: "You must enter a date",
}
});

Resources