问题
I created a custom Validator called threeNumbers
what it does is that it accepts only three digit numbers. But when I applied it to the username
field it throws an error ERROR TypeError: Cannot read property 'length' of null
and
formGroup expects a FormGroup instance. Please pass one in.
ngOnInit() {
// form controls validation specicified in the class for the Reactive Forms
this.courseForm = this.fb.group({
username: [null, [Validators.required, this.threeNumbers.bind(this)]],
email: [null, [Validators.required, Validators.pattern('([a-zA-Z0-9_.-]+)@([a-zA-Z0-9_.-]+)\\.([a-zA-Z]{2,5})')]],
address: [null, [Validators.required, Validators.minLength(10), Validators.maxLength(100)]],
select: [null, [Validators.required]]
});
this.dropDownArr = this.dropdown.getData();
// this.personDetail = {
// name: '',
// email: '',
// address: '',
// chosenCourse: ''
// };
this.personDetail = this.fieldData.getPersonData();
console.log(this.courseForm);
}
threeNumbers(control: FormControl) {
if (control.value.length < 3 && typeof control.value !== 'number') {
return { 'greater than 3 numbers': true };
}
return null;
}
//HTML template
<!-- Form with three inputs and one dropdown which intializes with data from service on intialization and validates with min and maxlength-->
<section class="container">
<!-- ngSubmit calls the function onSubmit on submitting the form -->
<form class="form-horizontal" (ngSubmit)='onSubmit()' [formGroup]='courseForm'>
<div class="form-group">
<label for="inputUsername" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputUsername" placeholder="Username" formControlName="username" name="name"
[ngClass]="{inValid: !courseForm.get('username').valid && courseForm.get('username').touched, valid: courseForm.get('username').valid && courseForm.get('username').touched}">
<span class="help-block" *ngIf="!courseForm.get('username').valid && courseForm.get('username').touched">Please enter a valid username</span>
</div>
</div>
<!-- username input ends here -->
<div class="form-group">
<label for="inputEmail" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<!-- CSS class applied based on validation -->
<input type="email" class="form-control" id="inputEmail" placeholder="Email" formControlName="email" name="email" [ngClass]="{inValid: !courseForm.get('email').valid && courseForm.get('email').touched, valid: courseForm.get('email').valid && courseForm.get('email').touched}">
<span class="help-block" *ngIf="!courseForm.get('email').valid && courseForm.get('email').touched">Please Enter a valid email</span>
</div>
</div>
<!-- email input ends here -->
<div class="form-group">
<label for="inputAddress" class="col-sm-2 control-label">Address</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputAddress" placeholder="Your Address" formControlName="address" name="address"
[ngClass]="{inValid: !courseForm.get('address').valid && courseForm.get('address').touched, valid: courseForm.get('address').valid && courseForm.get('address').touched}">
<!--Display error message on MinLength and MaxLength Validation-->
<span class="help-block" *ngIf="courseForm.get('address')?.errors?.required && courseForm.get('address').touched">Please Enter Your Address</span>
<span class="help-block" *ngIf="(courseForm.get('address')?.errors?.minlength?.requiredLength !== courseForm.get('address')?.errors?.minlength?.actualLength) && courseForm.get('address')?.touched">Address should be at least 10 characters long</span>
</div>
</div>
<!-- address input ends here -->
<div class="form-group">
<label for="sel1" class="col-sm-2 control-label">Choose Course</label>
<div class="col-sm-10">
<select class="form-control" id="sel1" formControlName="select" [(ngModel)]="selectedOption" name="select" [ngClass]="{inValid: !courseForm.get('select').valid && courseForm.get('select').touched, valid: courseForm.get('select').valid && courseForm.get('select').touched}">
<option [value]="selectedOption" [disabled]="true">Choose Your Course</option>
<option *ngFor="let data of dropDownArr; index as i" [ngValue]="data.course">{{data.course}}</option>
</select>
<span class="help-block" *ngIf="!courseForm.get('select').valid && courseForm.get('select').touched">Please choose a Course</span>
</div>
</div>
<!-- select input ends here -->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default" [disabled]=!courseForm.valid>Submit</button>
<button type="button" class="btn btn-default" (click)="resetForm(f)">Reset</button>
</div>
</div>
<!-- submit and reset buttons ends here -->
</form>
</section>
<!-- section displays the submited form data in the view -->
<section class="container">
<div class="panel panel-default">
<div class="panel-heading">Registered users</div>
<!-- List group -->
<ul class="list-group">
<li class="list-group-item" *ngFor="let person of personsList">username: {{person.name}} | email: {{person.email}}
| Address: {{person.address}} |
Course Chosen: {{person.chosenCourse}}</li>
</ul>
</div>
</section>
回答1:
Like the built-in validators, you do not need to call threeNumbers
, just pass it as a reference to this.fb.group
:
You do not need the bind
call, as Angular calls this internally. Change it to:
this.courseForm = this.fb.group({
username: ['', [Validators.required, this.threeNumbers]],
email: ['', [Validators.required, Validators.pattern('([a-zA-Z0-9_.-]+)@([a-zA-Z0-9_.-]+)\\.([a-zA-Z]{2,5})')]],
address: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(100)]],
select: ['', [Validators.required]]
});
At the moment, you're binding the this
property. In this instance, this
refers to your instantiated version of the class.
Remove the parenthesis essentially.
You could go the extra mile and make your threeNumbers
method static since a validator should not depend on any local variables - the Angular default validators are static.
public static ThreeNumbers(control: FormControl) {
if (control.value.length < 3 && typeof control.value !== 'number') {
return { 'greater than 3 numbers': true };
}
return null;
}
I would also recommend passing an empty string ''
, instead of null
to each control initialisation. This helps with a consistent state. N.b. on that note, you have a 'typeof' check, if this FormControl is bound to an input, then typeof
will always be a string.
I have made amends - see above.
来源:https://stackoverflow.com/questions/46447270/why-is-custom-validator-not-working