Angular 2 MDF validator to require atleast one of two fields - validation

I have two input fields: email and phone, if user enters input in email then phone input is not required and vice versa. Atleast one of the fields is required at a time. I have model driven form and I am trying to write a custom validator function for this. So far I have got:
In my validator.ts:
import { FormGroup, FormControl } from '#angular/forms';
export function requiredOptional(emailVal: string, phoneVal: string) {
return (group: FormGroup): {[key: string]: any} => {
let email = group.controls[emailVal];
let phone = group.controls[phoneVal];
if (!email.value|| !phone.value) {
return {
isRequired: true
};
}
}
}
In my testForm.component.ts:
export class TestFormComponent{
testForm: FormGroup;
constructor(public fb: FormBuilder) {
this.testForm= fb.group({
email: ['', emailValidator],
phone: ['', phoneValidator],
password: ['', Validators.required],
confirmPassword: ['', Validators.required]
}, {validator: matchingPasswords('password', 'confirmPassword')},
{validator: requiredOptional('email', 'phone')}
)
}
onSubmit(value: Object): void {
console.log(value);
}
}
and in my testForm.component.html:
<form [formGroup]="testForm" novalidate (ngSubmit)="testForm.valid && onSubmit(testForm.value)">
.......
<input type="email" placeholder="Email*" formControlName="email">
<div class="form-text error" *ngIf="testForm.controls.email.touched">
<div *ngIf="testForm.hasError('isRequired')">Email or Phone is required.</div>
<div *ngIf="testForm.controls.email.hasError('invalidEmail')">Email is invalid.</div>
</div>
.......
<input type="text" placeholder="Mobile Number*" formControlName="phone">
<div class="form-text error" *ngIf="testForm.controls.phone.touched">
<div *ngIf="testForm.hasError('isRequired')">Email or Phone is required.</div>
<div *ngIf="testForm.controls.phone.hasError('invalidPhone')">Phone is invalid.</div>
</div>
.......
<button [disabled]="!testForm.valid" type="submit" class="btn btn-primary pull-right">Sign up</button>
</form>
But this is not working, I am getting error in testForm.component.ts: Supplied parameters do not match any signature of call targets

You can define multiple validating functions by composing like following:
export class TestFormComponent{
testForm: FormGroup;
constructor(public fb: FormBuilder) {
this.testForm= fb.group({
email: ['', emailValidator],
phone: ['', phoneValidator],
password: ['', Validators.required],
confirmPassword: ['', Validators.required]
}, {validator: Validators.compose([matchingPasswords('password', 'confirmPassword'), requiredOptional('email', 'phone')]})
}
onSubmit(value: Object): void {
console.log(value);
}
}
Focus on following change:
{validator: Validators.compose([matchingPasswords('password', 'confirmPassword'), requiredOptional('email', 'phone')]}
Instead of setting matchingPasswords validator, we can set array of validating functions by using Validators.compose

Related

graphql - call mutation from a form

I'm new to graphql
I have a simple react app that lists books using a graphql query that queries a mongoDB database.
The schema contains a addBook Mutation that adds books to the DB.
This works using graphiql and I can add books and display them.
My problem now is I'm trying to use this mutation to add the books from a form on the react page.
I have a addBook component and listBooks component.
I get the error TypeError: this.props.addBookMutation is not a function
addBooks.js
import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import { addBookMutation } from '../queries/queries';
class AddBooks extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
genre: "",
author: "",
}
}
submitForm(e) {
e.preventDefault()
this.props.addBookMutation({
variables: {
name: this.state.name,
genre: this.state.genre,
author: this.state.author,
}
})
}
render() {
return (
<div className="wrapper">
<form action="" className="o-form" onSubmit={this.submitForm.bind(this)}>
<div className="o-form__element">
<label className="o-form__label" htmlFor="">Book Name</label>
<input className="o-form__input" type="text" onChange={(e) => this.setState({ name: e.target.value })} />
</div>
<div className="o-form__element">
<label className="o-form__label" htmlFor="">Description</label>
<textarea className="o-form__input" type="text" onChange={(e) => this.setState({ genre: e.target.value })}>
</textarea>
</div>
<div className="o-form__element">
<label className="o-form__label" htmlFor="">Year</label>
<input className="o-form__input" type="text" onChange={(e) => this.setState({ author: e.target.value })} />
</div>
<button className="o-form__btn">Add Book</button>
</form>
</div>
)
}
}
export default graphql(addBookMutation)(AddBooks)
queries.js
import { gql } from 'apollo-boost';
const getBookQuery = gql`
{
fonts{
name
genre
author
}
}
`
const addBookMutation = gql`
mutation($name: String!, $genre: String!, $author: String!){
addBook(
name: $name,
genre: $genre,
author: $author
)
}
`
export { getBookQuery, addBookMutation };
you can't call this.props.addBookMutation, in your case for a class component call it by this.props.mutate({}) for more info
submitForm(e) {
e.preventDefault();
this.props.mutate({
variables: {
name: this.state.name,
genre: this.state.genre,
author: this.state.author,
}
}).catch(res => {
const errors = res.graphQLErrors.map(err => err.message);
this.setState({ errors });
});
}

Cannot read error formControl

I had created a method to validate password and cofirmpassword in angular 2. I create a file password-validation.ts
import {AbstractControl} from '#angular/forms';
export class PasswordValidation {
static MatchPassword(_ac: AbstractControl) {
let password = _ac.get('password').value;
let confirmPassword = _ac.get('password_confirm').value;
if(password != confirmPassword) {
console.log('false');
_ac.get('password_confirm').setErrors( {"MatchPassword": true}
)
} else {
console.log('true');
return null
}
}
}
In register.ts
this.registerForm = this._fb.group({
firstname: [''],
lastname: [''],
email: [''],
password: [''],
password_confirm: []
},
{
validator: PasswordValidation.MatchPassword
});
And in html
<input type="password" placeholder="Password" required=" " formControlName="password">
<input type="password" placeholder="Password Confirmation" required=" " formControlName="password_confirm">
<div class="alert alert-danger" *ngIf="registerForm.controls.password_confirm.error?.MatchPassword">Password not match</div>
However I cannot show the alert. Maybe registerForm.controls.password_confirm.error?.MatchPassword don't handle.
What should I do in this case? Thanks everyone.
validator: PasswordValidation.MatchPassword.bind(this)
Does it help?

Handling Angular2 - 4 errors with nested form groups in reactive forms

Guys I need some help with Angular 4 reactive forms. I have nested form-groups also works fine except Validation. But my app is crashing when I try to add some handling validation in my html markup.
my component code looks like:
createForm() {
let options: any = {};
for (let option of this.annoucementOptions) {
options[option.title] = new FormControl(false);
}
let optionsFormGroup: FormGroup = new FormGroup(options);
this.annoucementForm = this.fb.group({
address: this.fb.group({
country: ['', [Validators.maxLength(50)]],
state: ['', [Validators.maxLength(50)]],
city: ['', [Validators.required, Validators.maxLength(50)]],
street: ['', [Validators.required, Validators.maxLength(50)]],
apartment: ['', [Validators.required, Validators.maxLength(50)]],
}),
description: this.fb.group({
title: ['', [Validators.required, Validators.maxLength(80)]],
shortDescription: [''],
livingPlaces: [''],
room: [''],
options: optionsFormGroup
}),
priceBlock: this.fb.group({
price: ['', [Validators.required, Validators.maxLength(80)]],
type: ['', [Validators.required, Validators.maxLength(80)]],
}),
});
}
and this is a piece of my template code:
<form class="form form-lg form-def" *ngIf="annoucementForm" (ngSubmit)="saveDetails(annoucementForm)" name="annoucementForm" [formGroup]="annoucementForm">
<div class="form-block" formGroupName="address">
<h2 class="form-block-heading flag-label primary">Address</h2>
<ul class="row form-list">
<li class="col-md-6 col-lg-4 form-list-item">
<md-input-container class="d-block">
<input type="text" mdInput placeholder="*Country" formControlName="country">
</md-input-container>
<div class="form-error text-danger"
*ngIf="annoucementForm.get('country').touched "
>
<p *ngIf="annoucementForm.get('country').hasError('maxlength')">
*This field be more than 35 characters long.
</p>
</div>
</li>
</ul>
Use
annoucementForm.get('address.country')
or
annoucementForm.get(['address', 'country'])
instead of
annoucementForm.get('country')

Angular 2 force custom validation on keyup

I have this code:
this.form = fb.group({
username: ['', Validators.compose([Validators.required])],
fullName: ['', Validators.compose([Validators.required])],
password: ['', Validators.compose([Validators.required])],
confirmPassword: ['', Validators.required],
}, {validator: matchingPasswords('password', 'confirmPassword')});
matchingPasswords:
export function matchingPasswords(passwordKey: string, passwordConfirmationKey: string) {
return (group: FormGroup) => {
let password = group.controls[passwordKey];
let passwordConfirmation = group.controls[passwordConfirmationKey];
if (password.value !== passwordConfirmation.value) {
return passwordConfirmation.setErrors({mismatchedPasswords: true})
}
}
}
html:
<div class="form-group">
<input [formControl]="confirmPassword" class="form-control checking-field" type="password">
<span class="help-block text-danger" *ngIf="form.get('password').touched && form.get('password').hasError('required')">
</div>
<div class="form-group">
<input class="custom-control-input checkbox-main" type="checkbox" [(ngModel)]="policyButtonValue" [ngModelOptions]="{standalone: true}" ngDefaultControl>
<span class="custom-control-indicator"></span>
</div>
this is functional, and works perfectly, but I have a special use-case scenario that should be fixed.
click in the first password field.
fill up a password, eg.: "foo"
click in the confirm password field.
tpye in the same thing, eg.:"foo"
till this point, no problems.
type something different into the confirm password field, eg: "foobar"
(at this point, the validator shows that there is an error here)
click in the password field
type in the same thing that is in the password field: "foobar"
and here, the confirm password field is still invalid, until the password field looses focus...
is there a way, to force the matchingPassword validation run on keyup event, rather than how it works now?
You need an else block:
if (password.value !== passwordConfirmation.value) {
passwordConfirmation.setErrors({mismatchedPasswords: true})
}
else {
passwordConfirmation.setErrors(null);
}

How to validate a form on button submit

I have a form, which is getting validated once the control gets the class change from ng-untouched ng-pristine ng-invalid to ng-pristine ng-invalid ng-touched but if i have a form with more controls then i want the user to know which field he/she has missed out on the button submit. how can i do that using angularJS2. I have used ReactiveFormsModule to validate the controls
The following is my code: component
import { Component } from '#angular/core';
import { FormBuilder, Validators, FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'page',
templateUrl:'../app/template.html'
})
export class AppComponent {
registrationForm: FormGroup;
constructor(private fb: FormBuilder) {
this.registrationForm = fb.group({
username: ['', [Validators.required, Validators.minLength(4)]],
emailId: ['', [Validators.required, this.emailValidator]],
phonenumber: ['', [Validators.required, this.phoneValidation]]
})
}
emailValidator(control: FormControl): { [key: string]: any } {
var emailRegexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+#[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
if (control.value && !emailRegexp.test(control.value)) {
return { invalidEmail: true };
}
}
phoneValidation(control: FormControl) {
if (control['touched'] && control['_value'] != '') {
if (!/^[1-9][0-9]{10}$/.test(control['_value'])) {
return { 'invalidPhone': true }
}
}
}
}
The following is my code: module
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { ReactiveFormsModule } from '#angular/forms';
import { AppComponent } from '../app/component';
#NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
The following is my code: template
<form [formGroup]="registrationForm" (ngSubmit)="registrationForm.valid && submitRegistration(registrationForm.value)">
<input type="text" formControlName="username" placeholder="username" />
<div class='form-text error' *ngIf="registrationForm.controls.username.touched">
<div *ngIf="registrationForm.controls.username.hasError('required')">Username is required.</div>
</div>
<br />
<input type="text" formControlName="emailId" placeholder="emailId" />
<div class='form-text error' *ngIf="registrationForm.controls.emailId.touched">
<div *ngIf="registrationForm.controls.emailId.hasError('required')">email id is required.</div>
<div *ngIf="registrationForm.controls.emailId.hasError('invalidEmail')">
Email is not in correct format.
</div>
</div>
<br />
<input type="text" formControlName="phonenumber" placeholder="phonenumber" />
<div class='form-text error' *ngIf="registrationForm.controls.phonenumber.touched">
<div *ngIf="registrationForm.controls.phonenumber.hasError('required')">phone number is required.</div>
<div *ngIf="registrationForm.controls.phonenumber.hasError('invalidPhone')">Invalid phone number.</div>
</div>
<br />
<input type="submit" value="Submit" />
</form>
I thought of updating all the invalid controls class to ng-untouched ng-pristine ng-invalid but not sure if this is the right approach
Angular forms don't provide ways to specify when to do validation.
Just set a flag to true when the form is submitted and show validation warnings only when the flag is true using *ngIf="flag" or similar.
<form [formGroup]="registrationForm" (ngSubmit)="submitRegistration(registrationForm.value)">
<div *ngIf="showValidation> validation errors here </div>
showValidation:boolean = false;
submitRegistration() {
if(this.registrationForm.status == 'VALID') {
this.processForm();
} else {
this.showValidation = true;
}
}

Resources