I am trying to implement a form in ionic 2 using the latest @angular /forms import. In the template I use
<form [formGroup]="clientFormGroup" (ngSubmit)="onSubmit(clientForm.value)">
<div formGroupName="clientFormGroupName">
<ion-input type="text" formControlName="client_name"></ion-input>
... etc
And in the ts file:
import { REACTIVE_FORM_DIRECTIVES, FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
templateUrl: 'build/pages/client-details/client-details.html',
directives: [REACTIVE_FORM_DIRECTIVES],
providers: [Database]
})
export class SomePage {
clientFormGroup = new FormGroup({
clientFormGroupName: new FormGroup({
client_name: new FormControl(),
anotherControl: new FormControl(),
etc...
})
})
Is it possible to add Validation to this, as used to be the case in the previous @angular /common form functionality using the FormBuilder?
FormBuilder is already there in @angular /forms
import { REACTIVE_FORM_DIRECTIVES, FormBuilder, FormGroup, Validators } from '@angular/forms';
....
form = FormGroup;
....
constructor(private fb: FormBuilder) {...}
this.form = this.fb.group({
clientFormGroupName: ['', Validator.required]
});
no need to call new FormControl
, the formBuilder does it internally
Thanks for that. Now Iâm getting errors for my template: e.g for one of the FormControls âŚ
<ion-item [class.error]="!client_name.valid && client_name.touched">
<ion-label floating class="required-label">Given name(s)</ion-label>
<ion-input type="text" FormControl="client_name"></ion-input>
</ion-item>
<div *ngIf="client_name.hasError('required') && client_name.touched" class="error-box">* Name is required!</div>
The error says
TypeError: undefined is not an object (evaluating âself.context.client_name.hasErrorâ)
It doesnât seem to recognise the client_name controlâŚ
OK, I got it going as follows:
EDIT: THIS IS NOT THE SOLUTION! Refer to the 7th post in this thread for a working solution.
import { REACTIVE_FORM_DIRECTIVES, FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms';
@Component({
templateUrl: 'build/pages/client-details/client-details.html',
directives: [REACTIVE_FORM_DIRECTIVES],
})
export class SomePage {
// declare the form
clientForm: FormGroup;
// declare all the form controls
client_title: any;
client_name: any;
...
constructor( ... private fb: FormBuilder){
this.clientForm = this.fb.group({
client_title: [''],
client_name: ['', Validators.compose([Validators.required, Validators.minLength(2)])],
...
});
// define Controls
this.client_title = this.clientForm.controls['client_title'];
...
this.client_name = this.clientForm.controls['client_name'];
}
Any simplifying suggestions welcome âŚ
there is nor FormControl directive ???
i think it is not necessary to create controls again on your own:
export class SomePage {
// declare the form
clientForm: FormGroup;
constructor( ... private fb: FormBuilder){
this.clientForm = this.fb.group({
client_title: [''],
client_name: ['', Validators.compose([Validators.required, Validators.minLength(2)])],
...
});
}
}
Template:
<form [formGroup]="clientForm">
<input
type="text"
formControlName="client_title"
>
<input
type="text"
formControlName="client_name"
>
</form>
I really appreciate you helping me out here. Your code works on its own. However the error occurs when I try to add validation. I.e.
<ion-item>
<ion-label floating class="required-label">Given name(s)</ion-label>
<ion-input type="text" formControlName="client_name"></ion-input>
</ion-item>
works but does no validation. While
<ion-item [class.error]="!client_name.valid && client_name.touched">-->
<ion-label floating class="required-label">Given name(s)</ion-label>
<ion-input type="text" formControlName="client_name"></ion-input>
</ion-item>
<div *ngIf="client_name.hasError('required') && client_name.touched" class="error-box">* Name is required!</div>
<div *ngIf="client_name.hasError('minlength') && client_name.touched" class="error-box">* Minimum name length is 2!</div>
throws the error I described above:
[Error] Error: Uncaught (in promise): EXCEPTION: Error in build/pages/client-details/client-details.html:31:13
ORIGINAL EXCEPTION: TypeError: undefined is not an object (evaluating âself.context.client_name.hasErrorâ)
âŚ
ERROR CONTEXT:
[object Object]
resolvePromise â zone.js:553
(anonymous function) â zone.js:589
invokeTask â zone.js:365
onInvokeTask â ng_zone_impl.js:44
invokeTask â zone.js:364
runTask â zone.js:265
drainMicroTaskQueue â zone.js:491
g â es6-shim.js:2194
(anonymous function) â es6-shim.js:2182
promiseReactionJob
I found the chapter âForms in Angular 2â from the ângBookâ very helpful. The following code worked.
<form [formGroup]="clientForm" (ngSubmit)="onSubmit(clientForm.value)">
<ion-item [class.error]="!client_name.valid && client_name.touched">
<ion-label floating class="required-label">Given name(s)</ion-label>
<ion-input type="text" formControlName="client_name"></ion-input>
</ion-item>
...
</form>
import { FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES, FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms';
@Component({
templateUrl: 'build/pages/client-details/client-details.html',
directives: [FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES]
})
export class ClientDetailsPage {
clientForm: FormGroup;
client_title: AbstractControl;
.... // repeat for other controls
constructor(private fb: FormBuilder, ...){
this.clientForm = this.fb.group({
client_name: ['', Validators.compose([Validators.required, Validators.minLength(2)])],
... // repeat for other controls
});
this.client_name = this.clientForm.controls['client_name'];
... // repeat for other controls
}
}
The Validation works now âŚ
1 Like
lannie
October 10, 2016, 4:30pm
8
This thread helped but I still had some frustration getting a form going under Iconic 2 RC0. In case it might help somebody, here is what I finally got working. It is a little unusual in that it doesnât have a submit button; instead, I am sending each change to my back end when the form field changes, using the observable and react. I went down this path because ionChange didnât seem to be working in an ion-input, and besides, I want to use the built-in for validators. (Embarrassed to show a couple of ugly timing hacks that I hope to get rid of at some point, but what the heck.)
<ion-item>
<ion-label floating primary>name</ion-label>
<ion-input formControlName="name" type="text" id="name" spellcheck="false" autocapitalize="off"></ion-input>
</ion-item>
<label *ngIf="!(nameCtrl.valid || nameCtrl.pristine)" class="danger" padding-left>Invalid name</label>
<ion-item>
<ion-label floating primary>description</ion-label>
<ion-input formControlName="description" type="text" id="description" spellcheck="false" autocapitalize="off"></ion-input>
</ion-item>
<label *ngIf="!(descriptionCtrl.valid || descriptionCtrl.pristine)" class="danger" padding-left>Invalid description</label>
</form>
import {Component} from '@angular/core';
import {Services} from '../../../services/services';
import {NavController, NavParams} from 'ionic-angular';
import {DataService} from '../../../services/dataService';
import { /*Http,*/ Response/*, Headers*/ } from '@angular/http';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/debounceTime';
import { Observable } from 'rxjs/Observable';
import {FormGroup, FormBuilder, FormControl, Validators} from '@angular/forms';
@Component({
templateUrl: 'port.html'
})
export class PortPage {
myForm: FormGroup;
nameCtrl: FormControl;
descriptionCtrl: FormControl;
id:number;
portData:any;
loading:boolean = true; // to block sending initialization changes back to host
portsCallback:any;
constructor(fb: FormBuilder, private services: Services, private navCtrl: NavController, public navParams: NavParams, private dataService: DataService) {
this.id = this.navParams.get('s').id;
this.nameCtrl = fb.control('', [Validators.required, Validators.minLength(3)]);
this.nameCtrl['name'] = 'name';
this.descriptionCtrl = fb.control('', [Validators.required, Validators.minLength(1)]);
this.descriptionCtrl['name'] = 'description';
this.myForm = fb.group({
'name': this.nameCtrl,
'description': this.descriptionCtrl
});
this.nameCtrl.valueChanges
.debounceTime(1000)
.subscribe(newValue => this.inputChange(this.nameCtrl));
this.descriptionCtrl.valueChanges
.debounceTime(1000)
.subscribe(newValue => this.inputChange(this.descriptionCtrl));
}
ngOnInit(refresher?:any) {
setTimeout(() => { // delay allows for services setup
this.getPortDetail(this.id).subscribe(() => {
this.nameCtrl.setValue(this.portData['name']);
this.descriptionCtrl.setValue(this.portData['description']);
});
},10);
setTimeout(() => { // delay blocks initial form setting from triggering updatePort()
this.loading = false;
},2000);
}
private updatePort(id:number, name:string, value:any){
this.dataService.Update('ports/' + id, '{' + name + ':' + value + '}').subscribe((res:any) => (res !== 200) && this.services.Alert('Update failed!','Pull page down to refresh'));
};
public inputChange(fc:FormControl) {
if (!this.loading) {
this.updatePort(this.id, fc['name'], fc.value);
}
}
}