Reactive Form (Cannot Read property error of null) - html

Error:
Cannot read property errors of null in form group.
I am creating a reactive form with some validation but i'm getting this error.Below is my whole form control
<div class="container mt-5">
<form
[formGroup] = 'loginForm'
(ngSubmit) = 'onSubmit()'
>
<div class="form-group">
<label>User Name</label>
<input
type="text" class="form-control"
placeholder="Enter User Name"
formControlName = "userName"
[ngClass]="{'is-invalid': (
loginForm.get('userName').dirty &&
!loginForm.get('userName').valid
)}"
>
<span class="invalid-feedback">
<span *ngIf = "loginForm.get('firstName').errors?.required">
Please Enter User Name.
</span>
</span>
</div>
<div class="form-group">
<label>Password</label>
<input
type="password"
class="form-control"
placeholder="Password"
formControlName = "password"
>
</div>
<button
type="submit"
class="btn btn-primary"
[disabled] = "!loginForm.valid">Submit</button>
</form>
{{loginForm.valid}}

Looking at your code, the possible reason for this error seems like you are accessing the firstName formcontrol when it is not defined in the loginForm.

It seems that you have gonna wrong with the formControlName, either it is not added in .ts file or by mistake you have mentioned 'firstName' in the error span
Replace firstName with userName in the error span
Please replace this code -
<span *ngIf = "loginForm.get('firstName').errors?.required">
Please Enter User Name.
</span>
With
<span *ngIf = "loginForm.get('userName').errors?.required"> // Here is the change
Please Enter User Name.
</span>

OMG, one hour working on it, realized I had
this.ContactInfoFG.get('phoneNumberFG') as FormGroup;
instead of
this.contactInfoFG.get('phoneNumberFG') as FormGroup;
Just check the characters for this issue nothing else!
Your solutions helped as well.

Related

Jasmine Angular testing if input would show required message

I am trying to check whether after I submit, login form without username / password, I will receive an error message below the certain field, and i done a simple test scenario for that:
it('password input should display required message', async() => {
component.validateForm.controls['password'].setValue('');
const submitBtn = el.querySelector("[data-test='submit']") as HTMLElement;
const control = el.querySelector("[data-test='password-control']") as HTMLElement;
submitBtn.click();
fixture.detectChanges();
expect(control.innerText).toContain('Please input your Password!');
});
html code:
<form nz-form [formGroup]="validateForm" class="login-form" (ngSubmit)="submitForm()" data-test="form">
<nz-form-item >
<nz-form-control nzErrorTip="Please input your username!" data-test="username-control">
<nz-input-group nzPrefixIcon="user" >
<input type="text" nz-input formControlName="username" placeholder="Username"/>
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control nzErrorTip="Please input your Password!" data-test="password-control">
<nz-input-group nzPrefixIcon="lock">
<input type="password" nz-input formControlName="password" placeholder="Password" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<div nz-row class="login-form-margin">
<div nz-col [nzSpan]="12">
<label nz-checkbox formControlName="remember">
<span>Remember me</span>
</label>
</div>
<div nz-col [nzSpan]="12">
<a class="login-form-forgot">Forgot password</a>
</div>
</div>
<button nz-button class="login-form-button login-form-margin" [nzType]="'primary'" data-test="submit">Log in</button>
Or
<a>register now!</a>
</form>
and when I tried this in browser by running in dev tools after submiting form without typing anything in password input
document.querySelector("[data-test='password-control']").includes('Please input your Password!') it returns true, I also tried adding to code above tick(1000), before expect statement to make sure, there is enough time to make it appear before the check, but it hasn't changed anything, the test result is negative with message Expected '' to contain 'Please input your Password!'..
Will be happy with any suggestions to solve that issue :)

Validation in forms with angular: how to show specific errors for different invalid cases?

I have a form which validates all the inputs. For, say, 'username' input I want to make sure it's a required field first and second that its length isn't less than 2 or more than 10 (or fits another pattern I use).
I'd like to show an error when either validation fails. How do I do this if I want to show a specific error for each case case? For example: if the input is empty show 'required', if the value is wrong show 'invalid'. Here's my code:
component:
companyNamePattern = "^[a-z0-9_-]{8,15}$";
pwdPattern = "^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{6,12}$";
emailPattern = "^[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,4}$";
this.addCompForm = fb.group({
'companyName': [null,Validators.compose([Validators.required,Validators.minLength(5),Validators.maxLength(20)])],
'companyPassword': [null,Validators.compose([Validators.required,Validators.minLength(5),Validators.maxLength(20)])],
'companyEmail': [null,Validators.compose([Validators.required,Validators.email])]
})
html:
<form [formGroup] ="addCompForm" (ngSubmit) = "addCompany(addCompForm.value)">
<p>Enter the company name:</p>
<input type="text" formControlName="companyName" >
<div class="alert" *ngIf="!addCompForm.controls['companyName'].valid && addCompForm.controls['companyName'].touched" >{{nameAlert}}</div>
<p>Enter the company password:</p>
<input type="text" formControlName="companyPassword">
<div class="alert" *ngIf="!addCompForm.controls['companyPassword'].valid && addCompForm.controls['companyPassword'].touched " >{{passAlert}}</div>
<p>Enter the company email:</p>
<input type="text" formControlName="companyEmail">
<div class="alert" *ngIf="!addCompForm.controls['companyEmail'].valid && addCompForm.controls['companyEmail'].touched " >{{emailAlert}}</div>
<br>
<br>
<div class="btn-container">
<button type="submit" [disabled]="!addCompForm.valid">
<i class="material-icons">done</i>
</button>
</div>
</form>
*ngIf="addCompForm.get('companyName').hasError('required')
add this to make required field and
*ngIf="addCompForm.get('companyName').hasError('minLength')
add this for making minLength and same as for max length .hasErro('maxlength')

input field or help text doesn't turn red when field is invalid

I used to implement an Angular 2/4 application with Bootstrap 3 and used the Reactive Forms approach. I had a field-validation where the border of the input-field turned red and an error message appeared under the field in red font color.
it looks like this:
<div class="form-group row"
[ngClass]="{'has-error': (sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
!sourcesForm.get('sourceName').valid }">
<label class="col-md-2 col-form-label"
for="sourceNameId">Source Name</label>
<div class="col-md-8">
<input class="form-control"
id="sourceNameId"
type="text"
placeholder="Source Name (required)"
formControlName="sourceName" />
<span class="help-block" *ngIf="(sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
sourcesForm.get('sourceName').errors">
<span *ngIf="sourcesForm.get('sourceName').errors.required">
Please enter the Source Name.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.minlength">
The Source Name must be longer than 3 characters.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.maxlength">
The Source Name is too long.
</span>
</span>
</div>
</div>
Now i have to use Bootstrap 4 and neither the error message or the input-field turns red. How do i realise this? I tried to change the class of the parent span-block to "form-text" but it didn't work.
For beta version of Bootstrap v4, you can check out Form validation docs. There you can read about the new way, supported by all modern browsers for HTML5 way of form-validation with valid/invalid css classes. There Bootstrap uses the .was-validated and .invalid-feedback classes for what you want to achieve (see code snippet).
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<form class="container" id="needs-validation" novalidate>
<label for="validationCustom02">Last name</label>
<input type="text" class="form-control" id="validationCustom02" placeholder="Last name" value="Otto" required>
<label for="validationCustom03">City</label>
<input type="text" class="form-control" id="validationCustom03" placeholder="City" required>
<div class="invalid-feedback">
Please provide a valid city.
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
<script>
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
"use strict";
window.addEventListener("load", function() {
var form = document.getElementById("needs-validation");
form.addEventListener("submit", function(event) {
if (form.checkValidity() == false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add("was-validated");
}, false);
}, false);
}());
</script>
If you want something more similar to Bootstrap 3, you can use what they call server-side validation, as it is written:
As a fallback, .is-invalid and .is-valid classes may be used instead of the pseudo-classes for server side validation. They do not require a .was-validated parent class.
Previous answer for alpha version of Bootstrap V4 (if you must use this).
On Bootstrap V4 Form Validation Docs there is the following example:
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="form-group has-danger">
<label class="form-control-label" for="inputDanger1">Input with danger</label>
<input type="text" class="form-control form-control-danger" id="inputDanger1">
<div class="form-control-feedback">Sorry, that username's taken. Try another?</div>
<small class="form-text text-muted">Example help text that remains unchanged.</small>
</div>
So i think you just need to change the has-error class to has-danger
This is the solution:
<div class="form-group row">
<label class="col-md-2 col-form-label"
for="sourceNameId">Source Name</label>
<div class="col-md-8">
<input class="form-control"
[ngClass]="{'is-invalid': (sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
!sourcesForm.get('sourceName').valid }"
id="sourceNameId"
type="text"
placeholder="Source Name (required)"
formControlName="sourceName" >
<span class="invalid-feedback" *ngIf="(sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
sourcesForm.get('sourceName').errors">
<span *ngIf="sourcesForm.get('sourceName').errors.required">
Please enter the Source Name.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.minlength">
The Source Name must be longer than 3 characters.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.maxlength">
The Source Name is too long.
</span>
</span>
</div>
</div>
i needed to put the [ngClass]into the input-tag. Then i had to define the class as is-invalid and set the parent span-class to invalid-feedback
i know that your question is for long time ago, but it is the best way to validate the form-control input field by reactive form technique and bootstrap 4 to display the validation. first you need to write some code for your form :
in html section:
<form [formGroup]="myForm">
<div class="form-group">
<label for="name">first Name: </label>
<input type="text" class="form-control" formControlName="firstName" id="name">
<div *ngIf="firstName.touched && firstName.invalid" class="alert alert-danger">
<div *ngIf="firstName.errors.required">filling name is required!</div>
</div>
</div>
in ts file, you should implement the logic to conduct the validation.
in ts file:
myForm = new FormGroup({
'firstName':new FormControl('',Validators.required)
})
//getter method
get firstName(){
this.myForm.get('firstName');
}
now you can see that the validation is working. now to give style to input field to show the red border around the invalid input, just go to css file of component and add this class to the css file:
.form-control.ng-touched.ng-invalid{border:2px solid red;}
and simply you can see the result.

model driven form: validation not working as expected in Angular 2

I am using the model driven form like this .
Just like normal validations , i want that i show an error message if username and password are missing.
And Submit button should be disabled as long as username and password are not valid.
<div class="login">
<form #f="ngForm" (ngSubmit)="dologin(f)">
<div class="form-group">
<label for="username">Username</label>
<input id="username" type="text" class="form-control" name ="username" ngModel #username="ngModel">
<div [hidden]="username.valid" class="alert alert-danger"> Username is required.</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input id="password" type="password" class="form-control" name ="password" ngModel #password="ngModel">
<div [hidden]="password.valid" class="alert alert-danger"> Password is required.</div>
</div>
<button type="submit" [disabled]="username.length<0 || password.length<0" class="btn btn-primary" type="submit">Login</button>
</form>
</div>
i am seeing quite strange behaviour from validation div. Sometimes
it is showing "Password is required" and sometimes not.
i want to disable the submit button, until the form is valid .i tried
[disabled]="!f.valid"
but as i print it out f is always valid even
though i have not entered any data in username and password.
Component:
constructor(private router: Router,private authenticationService : AuthenticationService,private httpService:HttpService,private formBuilder:FormBuilder) {
this.form=formBuilder.group({
username:['',Validators.required],
password:['',Validators.required]
});
}
UPDATE
Can't bind to 'formGroup' since it isn't a known property of 'form'.
(" ][formGroup]="form"
(ngSubmit)="dologin(form.value)">
][formControl]="form.controls['password']">
[ERROR ->]
Username
[ERROR ->]
"): LoginComponent#4:8 No provider for NgControl ("
Password
[ERROR ->] ; Task: Promise.then ; Value:
Error: Template parse errors:(…) Error: Template parse errors: Can't
bind to 'formGroup' since it isn't a known property of 'form'. ("
][formGroup]="form"
(ngSubmit)="dologin(form.value)">
][formControl]="form.controls['password']">
[ERROR ->]
Username
[ERROR ->]
Thanks.
The way you have set up your HTML template is missing some key bits that actually ensure you have wired up the front end to the back end for a reactive form. What you have appears to be more in line with a template driven form mixed with model driven. In fact, the template you have posted will not even compile if you remove your FormsModule import.
To begin with remove your FormsModule import which is letting you mix the two different form types together. This will take us down a path where a strict Reactive Forms (aka model driven) implementation is required.
<form #f="ngForm" (ngSubmit)="dologin(f)"> will be changed to <form [formGroup]="form" (ngSubmit="dologin(form.value)"
Each of your inputs and warning divs will change from
<input id="username" type="text" class="form-control" name="username" ngModel #username="ngModel">
<div [hidden]="username.valid" class="alert alert-danger"> Username is required.</div>
To
<input id="username" type="text" class="form-control" name="username" formControlName="username">
The changes are because the ngModel attribute and #[name]="ngModel" are not supported in the model driven form, so instead you will use either formControlName or [formControl] syntax.
<div [hidden]="form.controls['username'].valid || form.controls['username'].pristine"
class="alert alert-danger"> Username is required.</div>
Finally, your submit button changes, note that you have type="submit" twice, from <button type="submit" [disabled]="username.length<0 || password.length<0" class="btn btn-primary" type="submit">Login</button>
To
<button type="submit" [disabled]="!form.valid" class="btn btn-primary">Login</button>
since we have successfully wired up the rest of the form the validation on the form group will now be correct
And here is a working plunker that you can play around with: https://plnkr.co/edit/Mu9vEYGB35SwUr9TEsPI?p=preview
Implementation without form builder
<form #loginForm="ngForm" (ngSubmit)="login()">
<md-input required type="email"
pattern="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
placeholder="Email Address" #email="ngModel" name="email"
[(ngModel)]="loginModel.email"></md-input>
<div *ngIf="email.dirty && !email.valid && email.touched && email.errors" class="error-message">
<div *ngIf="email.errors.required">Email is required</div>
<div *ngIf="!email.errors.required && email.errors.pattern">This is not a valid email</div>
</div>
<md-input required type="password" placeholder="Password" #password="ngModel" name="password" [(ngModel)]="loginModel.password"></md-input>
<div *ngIf="password.dirty && !password.valid && password.touched && password.errors" class="error-message">
<div *ngIf="password.errors.required">Password is required</div>
</div>
<button ma-raised-button [disabled]="!loginForm.valid">
Login
</button>
</form>
Component:
ngOnInit() {
this.loginModel = {email: '', password: ''};
}
login() {
console.log(this.loginModel['email']);
console.log(this.loginModel['password']);
}

ngSubmit HTML attribute using angular template variable

I have two methods ngMake and ngUpdate. I have one form, PostForm. I want to reuse the same form, but add different functionality depending on the url.
I gain information about the url using
Controller
if ($location.path() === '/makepost') {
$scope.FormTitle = 'Make a Post';
$scope.FormAction = 'server/blog/makepost.php';
$scope.FormMethod = 'POST';
$scope.FormSubmit = "ngMake()"
};
if ($location.path().indexOf('update') !== -1) {
$scope.FormTitle = 'Update a Post';
$scope.FormAction = null;
$scope.FormMethod = 'POST';
$scope.FormSubmit = "ngUpdate()";
};
HTML Form
<div ng-controller="BlogController as blog">
<h3 class="text-center">{{FormTitle}}</h3>
<form ng-show='user != null' ng-submit="{{FormSubmit}}" role="form" class="form-group" name="PostForm">
<label>Title: </label>
<div ng-class="(PostForm.Title.$dirty && PostForm.Title.$invalid) ? 'has-warning' : 'has-success'" class="form-group has-feedback">
<input data-ng-model="post.Title" data-ng-minlength="3" data-ng-maxlength="255" name="Title" type="text" class="form-control" placeholder="Title" required/>
<span ng-class="(PostForm.Title.$dirty && PostForm.Title.$invalid) ? 'glyphicon-warning-sign' : 'glyphicon-ok'" class="glyphicon form-control-feedback"></span>
</div>
<label>Content: </label>
<div ng-class="(PostForm.Content.$dirty && PostForm.Content.$invalid) ? 'has-warning' : 'has-success'" class="form-group has-feedback">
<textarea data-ng-model="post.Content" rows="8" name="Content" type="text" class="form-control" placeholder="Content" required></textarea>
<span ng-class="(PostForm.Content.$dirty && PostForm.Content.$invalid) ? 'glyphicon-warning-sign' : 'glyphicon-ok'" class="glyphicon form-control-feedback"></span>
</div>
<div ng-controller="AuthController as auth">
<input class="ng-hide" type="number" data-ng-model="post.UserID" name="UserID" value="{{user.ID}}">
</div>
<br>
<input type="submit" ng-class="(PostForm.$valid) ? 'btn-success' : 'disabled'" class="btn btn-block btn-default">
</form>
<p ng-show='user == null' class="text-center">You must be logged in in order to {{FormTitle | lowercase}}</p>
Explination
The {{FormSubmit}} template variable probably executes afterwards and causes a problem which doesn't permit the form to execute. I am open to suggestions, I want to reuse the same form. I read that ngSubmit requires a type="submit" button or input element contained within the form tags, I have that. I do not have any ng-clicks which might hinder the form.
I am open to suggestions
If there are any other problems with the form or the entire project please let me know, even if it is just a "bette practice".
The full project
https://github.com/AquaSolid/RAMA_Angular_PHP
Basically, I wised up. I contained the logic in the back-end. I created a function to choose which function to use. Meanwhile the the form contains the attribute ng-submit="chooseSubmit()". That's about it..
$scope.chooseSubmit = function() {
if ($scope.FormSubmit) {
if ($scope.FormSubmit === 'ngMake()') {
$scope.ngMake();
} else {
$scope.ngUpdate();
};
}
};