0

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>

5 Answers 5

3

User form array name

  <mat-form-field style="width: 100%;">
       <label>
       <input matInput placeholder="{{ getTranslateKey('startingRange.label') | translate }}" value="" formControlName="startingRange">
       </label>
      <mat-error *ngIf="ranges.get('startingRange').invalid">
         <p>IP address is not valid.</p>
      </mat-error>
   </mat-form-field>
Sign up to request clarification or add additional context in comments.

2 Comments

After trying all the other answers this is the one that gave me the correct behaviour without any errors. Thanks!
Happy it helped you
0

just change

<mat-error *ngIf="endingRange.invalid">
    <p>IP address is not valid.</p>
</mat-error>

to

<mat-error *ngIf="endingRange && endingRange.invalid"> <p>IP address is not valid.</p> </mat-error>

2 Comments

That's what we have the ? operator for: *ngIf="endingRange?.invalid"
Why exactly does this fix the problem?
0

The endingRange doesnt exist on ipRangeForm, it exists on the ipRanges members.

this.ipRangeForm = this.formBuilder.group(
    {
        ipRanges: this.formBuilder.array([])
    }
);

You need to attach the validation to each of the formArray members

Comments

0

I think there's a tiny delay between first get calling and formGroup initialization. to ignore that; just put question mark to check endingRange.

<mat-error *ngIf="endingRange?.invalid">
    <p>IP address is not valid.</p>
</mat-error>

So, this'll make angular check for endingRange before check invalid property.

Comments

0

The issue is that you’re accessing ipRanges as though it isn’t an array when it is. Angular has no way of knowing which IP Range in the array you want the end range of. Try this instead:

getEndingRange(idx:number) {
  return this.ipRangeForms.controls[idx].get(‘endingRange’);
}

And in template, feed the index into the function.

<mat-error *ngIf="getEndingRange(i).invalid">

Do the same with start ranges

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.