I have a reactive form within my Angular app that is being used to receive IP address ranges from the user. I am making use of a reactive form so that the user is able to enter and remove IP ranges at their own leisure.
The form itself makes use of a FormBuilder to create a FormArray with two form controls that are text inputs. All of the code to create the form and each form array element is relatively simplistic. However there seems to be an issue with something to do with displaying validation errors within my form. Specifically the lines
<mat-error *ngIf="endingRange.invalid">
<p>IP address is not valid.</p>
</mat-error>
If I comment out these lines the form works properly without any issues. However if I have the lines uncommented then I start to get the following error:
TypeError: Cannot read property 'invalid' of null
This doesn't really make much sense to me as I have a getting for this particular form control declared within my component file like this
get endingRange() {
return this.ipRangeForm.get('endingRange');
}
Another aspect that may be an issue is that the form is initially hidden from the view and is enabled through the use of a toggle. I'm not sure if it would be causing the errors but it's worth mentioning. You can see the logic for this in the below files.
Here are the full files being used.
SettingsComponent
export class SettingsComponent implements OnInit {
ipRestrictionEnabled: boolean;
ipRangeForm: FormGroup;
constructor(
baseService: PageService, private formBuilder: FormBuilder
) {
super(baseService);
}
ngOnInit() {
this.ipRestrictionEnabled = false;
this.ipRangeForm = this.formBuilder.group(
{
ipRanges: this.formBuilder.array([])
}
);
}
ngOnDestroy() {
this.destroy$.next(true);
}
getPageConfig() {
return {
pageKey: 'settings',
displaySidebar: true,
displayToolbar: true,
backButtonRoute: ''
};
}
toggleIpRestriction(): void {
this.ipRestrictionEnabled = !this.ipRestrictionEnabled;
}
get ipRangeForms() {
return this.ipRangeForm.get('ipRanges') as FormArray;
}
addRange() {
const ipRange = this.formBuilder.group({
startingRange: ['',
[
Validators.required,
// tslint:disable-next-line:max-line-length
Validators.pattern('^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$')
]
],
endingRange: ['',
[
Validators.required,
// tslint:disable-next-line:max-line-length
Validators.pattern('^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$')
]
]
});
this.ipRangeForms.push(ipRange);
}
get startingRange() {
return this.ipRangeForm.get('ipRanges').get('startingRange');
}
get endingRange() {
return this.ipRangeForm.get('ipRanges').get('endingRange');
}
deleteRange(index) {
this.ipRangeForms.removeAt(index);
}
submitIpRanges() {
console.log(this.ipRangeForms.getRawValue());
}
}
SettingsComponent
<ng-container *ngIf="ipRestrictionEnabled">
<div class="row mt-xl">
<div class="col-12">
<h4>{{ getTranslateKey('ipRanges') | translate }}</h4>
<p>{{ getTranslateKey('ipRangesDescription') | translate }}</p>
</div>
</div>
<div class="row">
<div class="col-12">
<form [formGroup]="ipRangeForm">
<div formArrayName="ipRanges">
<div *ngFor="let ranges of ipRangeForms.controls; let i=index" [formGroupName]="i">
<div class="row mb-lg">
<div class="col-6">
<mat-card>
<div class="row">
<div class="col-5">
<mat-form-field style="width: 100%;">
<label>
<input matInput placeholder="{{ getTranslateKey('startingRange.label') | translate }}" value="" formControlName="startingRange">
</label>
<mat-error *ngIf="startingRange.invalid">
<p>IP address is not valid.</p>
</mat-error>
</mat-form-field>
</div>
<div class="col-5">
<mat-form-field style="width: 100%;">
<label>
<input matInput placeholder="{{ getTranslateKey('endingRange.label') | translate }}" value="" formControlName="endingRange">
</label>
<!-- <mat-error *ngIf="endingRange.invalid">-->
<!-- <p>IP address is not valid.</p>-->
<!-- </mat-error>-->
</mat-form-field>
</div>
<div class="col-2 remove-column">
<button swui-core-button (click)="deleteRange(i)" class="mr-sm pd-zero"
color="secondary" buttonStyle="link">
{{ getTranslateKey('remove') | translate }}
</button>
</div>
</div>
</mat-card>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-12">
<button swui-core-button (click)="addRange()" class="mr-sm" color="secondary" buttonStyle="link">
<mat-icon class="button-icon">add</mat-icon>
{{ getTranslateKey('addIP') | translate }}
</button>
</div>
</div>
</ng-container>