Angular 2 form validation, hasError is not a function - forms

i try to make a validation for my input fields.
this is a piece of code that I used:
DepartmentComponent
import {
FORM_DIRECTIVES,
FormBuilder,
ControlGroup,
Validators ,
AbstractControl
} from 'angular2/common';
#Component({
selector: 'my-departments',
providers: [HTTP_PROVIDERS, DepartmentService],
directives: [FORM_DIRECTIVES, Alert],
styleUrls: ['app/department.component.css'],
templateUrl: 'app/department.component.html',
pipes:[SearchPipe]
})
export class DepartmentComponent implements OnInit {
myForm: ControlGroup;
departmentName: AbstractControl;
departmentLocation: AbstractControl;
constructor(private _router: Router, private _departmentService: DepartmentService, fb: FormBuilder) {
this.myForm = fb.group({
'departmentName': ['', Validators.required],
'departmentLocation': ['', Validators.required]
});
this.departmentName= this.myForm.controls['departmentName'];
this.departmentLocation= this.myForm.controls['departmentLocation'];
}
DepartmentComponent template
<form [ngFormModel]="myForm"
(ngSubmit)="addDepartment(newItem)" [hidden]="!showAddView" align="center">
<div>
<label for="editAbrv">Department name:</label><br>
<input type="text" [(ngModel)]="newItem.departmentName" [ngFormControl]="myForm.controls['departmentName']" >
<div *ngIf="departmentName.hasError('required')" class="ui error message"><b style="color:red;">Name is required</b></div>
</div>
<div>
<label for="editAbrv">Department Location:</label><br>
<input type="text" [(ngModel)]="newItem.departmentLocation" [ngFormControl]="myForm.controls['departmentLocation']" >
<div *ngIf="departmentLocation.hasError('required')" class="ui error message"><b style="color:red;">Location is required</b></div>
</div>
<div>
<button type="submit" class="ui button">Submit</button>
<button><a href="javascript:void(0);" (click)="showHide($event)" >
Cancel
</a></button>
</div>
</form>
The problem is that I got an error: .hasError is not a function. hasError function is in my html file (which you can see) I really don't see where I'm wrong. I did everything like is described in tutorial, but can't figure out why is this happen. Thanks for advice!

you should use *ngIf="myForm.controls['departmentLocation'].hasError('required')"
or any better luck with
this.departmentName= this.myForm.controls.find('departmentName'); ?

In place of hasError you should use errors
i.e it should be
myForm.controls['departmentLocation'].errors['required']
i.e with *ngIf
*ngIf="myForm.controls['departmentLocation'].errors['required']"

This is similar to another answer I've provided here: Form Builder with hasError() for validation throws an error of ERROR TypeError: Cannot read property 'hasError' of undefined.
The gist is that TypeScript getter's can be used to solve this in a clean way.
In your component class:
get departmentLocation() {
return this.myForm.get( 'departmentLocation' );
}
In your component template:
<input type="text" formControlName="departmentLocation">
<p class="ui error message" *ngIf="departmentLocation.hasError('required')">Department location is required</p>
Also important to note that using ngModel with Reactive Forms in Angular 6+ is depracated, so it's been removed from the examples above.

i was getting compilation error for using
loginForm.get('email').hasError('required')
This fixed it
loginForm.get('email')?.hasError('required')
use it like this
<mat-error *ngIf="!loginForm.get('password')?.hasError('required')
&& loginForm.get('password')?.hasError('whitespace')">

Related

Angular validation message for maxlength attribute

I'm having some trouble with displaying error messages for the maxlength attribute in Angular.
Problem
Since the maxlength attribute don't allow more characters than the specified amount, I'm having trouble displaying my error message. Is there any way to turn of the default behavior (allow the user to type more characters), in order to display my error message.
Code for textarea
<textarea maxlength="10"
[(ngModel)]="title.value"
#title="ngModel"></textarea>
Code for Angular validation
<div *ngIf="title.errors && (title.dirty || title.touched)"
class="alert alert-danger">
<div [hidden]="!title.errors.maxlength">
Only 10 characters allowed.
</div>
</div>
If you want me to provide any additional information, please let me know.
you can work with Reactive forms to validate properly your form,
here is a simple example how to use reactive forms :
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'title-form',
template: `
<form novalidate [formGroup]="myForm">
<label>
<span>Full title</span>
<input type="text" placeholder="title.." formControlName="title">
</label>
<div*ngIf="myForm.controls['title'].touched && myForm.get('title').hasError('required')">
Name is required
</div>
<div *ngIf="myForm.controls['title'].touched && myForm.controls['title'].hasError('maxlength')">
Maximum of 10 characters
</div>
</form>
`
})
export class TitleFormComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.myForm = this.fb.group({
title: ['', [Validators.required, Validators.maxLength(10)]],
});
}
}
hope it helps u :)
You can achieve it by setting the condition directly on the length of the input. A span tag with *ngIf can show/hide the error message:
HTML
<textarea class="form-control" id="title"
type="number" name="title" [(ngModel)]="titleModel"></textarea>
<span style="color:red" *ngIf="titleModel?.length > 10">
10 max
</span>
Class:
...
titleModel = 'I have more than 10 characters'
...
DEMO

Form in form. Can there be inheritance of form controls?

I have two components: ParentComponent and ChildComponent:
parent.component.ts
<form #form="ngForm" (ngSubmit)="onSubmit(form)" novalidate>
<input type="text" name="firstControl" [(ngModel)]="firstControl" />
<input type="text" name="secondControl" [(ngModel)]="secondControl" />
<child-component>
</form>
{{form.value | json}}
child.component.ts
<input type="text" name="thirdControl" [(ngModel)]="thirdControl" />
<input type="text" name="fourthControl" [(ngModel)]="fourthControl" />
Now, {{form.value | json}} returns { "firstControl": "", "secondControl": "" } and it's clear. My question is: Is there a way to form enherit form controls from child component? What is the correct way to get { "firstControl": "", "secondControl": "", "thirdControl": "", "fourthControl": "" } for ParentComponent? Is it possible?
Update:
Indeed there is an easier way:
import { FormsModule, ControlContainer, NgForm, NgModel } from '#angular/forms';
#Component({
...
viewProviders: [ { provide: ControlContainer, useExisting: NgForm } ]
})
export class ChildComponent {}
See also
Angular2 nested template driven form
Previous version:
I would say it's possible. For example you could add the following code to your
child.component.ts
import { NgForm, NgModel } from '#angular/forms';
#Component({
selector: 'child-component',
template: `
<input type="text" name="thirdControl" [(ngModel)]="thirdControl" />
<input type="text" name="fourthControl" [(ngModel)]="fourthControl" />
`
})
export class ChildComponent {
#ViewChildren(NgModel) ngModels: QueryList<NgModel>;
constructor(#Optional() private ngForm: NgForm) {}
ngAfterViewInit() {
if (this.ngForm) {
this.ngModels.forEach(model => this.ngForm.addControl(model));
}
}
}
Plunker Example
Angular DI system gives us the opportunity to get reference to parent NgForm instance because angular dependency resolution algorithm starts with current node and goes up through tree of elements. In my example we can imagine the follwing tree
#NgModule providers
|
my-app
|
form
/ | \
input[text] input[text] child-component
So when we are requiring NgForm token angular will search it in the next order
child-component
||
\/
form
||
\/
my-app
||
\/
#NgModule
On form element NgForm directive is placed so when can get it. Also we can get any token that was declared on NgForm directive within providers array. And this rule applies to any node.
See also Angular 2 - How does ng-bootstrap provide the NgbRadioGroup and NgbButtonLabel to their NgbRadio directive?
Then i just added child NgModel directives manually to NgForm so they should work together.
Angular has two kinds of forms, Template Driven and Reactive. What you want to do isn't possible with Template Driven forms, you'll need to look into Reactive Forms
In general it works by creating a form object in the component, and using it in the template. Angular says this is actually more flexible and easier to test, but I think the syntax is a lot harder to wrap your head around.
You'll want this in your parent component:
myForm: FormGroup;
constructor(private fb: FormBuilder) { // <--- inject FormBuilder
this.createForm();
}
createForm() {
this.myForm = this.fb.group({
firstControl: '',
secondControl: '',
thirdControl: '',
fourthControl: ''
});
}
Your parent template would look something like this:
<form [formGroup]="myForm" novalidate>
<input type="text"formControlName="firstControl" />
<input type="text" formControlName="secondControl" />
<child-component [parentFormGroup]="myForm">
</form>
And your child will accept the form group as an input, and the template will look something like this:
<div class="form-group" [formGroup]="parentFormGroup">
<input type="text" formControlName="thirdControl" />
<input type="text" formControlName="fourthControl" />
</div>

Angular 4 form validation

How to validate a form in angular 4 by clicking on external link (ie out side from tag). If the form is valid do some actions with form data else show validation messages. If form is valid I don't want to submit the form just need to get the form field values.
This is my answer post to another question:
The easy way is to use reactive forms, like this:
Code:
import {ReactiveForm, FormBuilder, Validators} from '#angular/form';
export class SignupFormComponent implements OnInit {
userForm: FormGroup;
firstName: string;
constructor(private _formBuilder:FormBuilder){}
ngOnInit() {
this.userForm = this._formBuilder.group({
'firstName': ['',[Validators.required,Validators.minLength(5)]]
});
}
onSubmit() {
console.log(this.firstName);
}
}
HTML:
<form [formGroup]="userForm" (ngSubmit)="onSubmit()" name="userForm">
<div class="form-group">
<label>First Name</label>
<input type="text" [(ngModel)]="firstName" class="form-control" formControlName="firstName">
<p *ngIf="userForm.controls.firstName.invalid && (userForm.controls.firstName.dirty || userForm.controls.firstName.touched)"> Error message </p>
</div>
<button type="submit" class="btn btn-primary" [disabled]="userForm.invalid">Submit </button>
</form>

Edit form field labels in angular 2

I have a built a model-driven (reactive) form, as shown here, in Angular 2.
My html looks like this:
<form [formGroup]="userForm" (ngSubmit)="onSubmit(userForm.value, userForm.valid)">
<label for="firstName">First Name:</label>
<input type="text" formControlName="firstname" id="firstName" required>
<label for="lastname">Last Name:</label>
<input type="text" formControlName="lastname" id="lastName" required>
<br>
<label for="email">Email:</label>
<input type="email" formControlName="email" id="email">
<br>
</form>
In my .ts file:
import { FormGroup, FormControl, FormBuilder, Validators } from '#angular/forms';
...
ngOnInit() {
this.paymentForm = this.formBuilder.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
email: ['',],
})
this.userForm.valueChanges.subscribe(value => {
console.log(value);
});
}
I've added the required attribute in my template as well, as suggested by angular docs
Quoting:
required remains, not for validation purposes (we'll cover that in the code), but rather for css styling and accessibility.
What I want is to cycle through each form field and add a * to the associated label if the field is required.
So, First Name reads First Name *; and so on.
How would I go about doing that. Thanks.
#Directive({
selector: '[required]'
})
export class LabelRequiredDirective {
constructor(private elRef:ElementRef){}
ngAfterContentInit() {
this.elRef.nativeElement.labels.forEach(l => l.textContent += ' *');
}
}
Because the selector matches every element that has the required attribute, it is applied to all elements where the label should be updated.
Sadly nativeElement.labels is only supported in Chrome. For other browsers another strategy is necessary to get the label associated with an input element (See also Find html label associated with a given input)

Angular2 Forms - ngControl

I'm trying to use ngControl to apply error classes based on user's input.
Somehow, I can't make it to work. I see that appropriate classes are set (line ng-invalid), but when trying to use name.valid (where name is my ngControl) it doesn't work.
html:
<div ngClass="{alert: name.invalid}">
<label for="name">Name</label>
<input ngControl="name" #name id="name" [(ngModel)]="user.name"/>
</div>
</div>
js
export class App {
userForm: any;
user: any;
constructor(
private _formBuilder: FormBuilder) {
this.user = {name: 'Ben'};
this.userForm = this._formBuilder.group({
'name': ['', Validators.required]
});
}
}
I saw on angular.io examples that they do use it like this (just for other cases, like show/hide divs)?
Here's the simple plunker: http://plnkr.co/edit/BKx4yplIOu44tk7Mfolc?p=preview
When input field is empty, I would expect that upper div gets alert class, but that doesn't happen.
In fact there are three things to change in your template:
ngClass should be [ngClass]. Otherwise the value is considered as a string and not as an expression.
#name should be #name="ngForm". Otherwise you reference the DOM element and not the control.
there is no invalid property on controls in Angular2 but only a valid one.
Here is the refactored code:
<div [ngClass]="{alert: !name.valid}">
<label for="name">Name</label>
<input ngControl="name" #name="ngForm"
required id="name" [(ngModel)]="user.name"/>
</div>
Here is the plunkr: http://plnkr.co/edit/OJfb9VDqlrRH4oHXQJyg?p=preview.
Note that you can't leverage of FormBuilder with ngControl since the latter allows you to define inline form. With FormBuilder you must use ngFormControl instead.
Here is a sample:
<div [ngClass]="{alert: !userForm.controls.name.valid}">
<label for="name">Name</label>
<input [ngFormControl]="userForm.controls.name"
id="name" [(ngModel)]="user.name"/>
</div>
See this article for more details:
http://restlet.com/blog/2016/02/11/implementing-angular2-forms-beyond-basics-part-1/