Upload
eyal-vardi
View
1.714
Download
2
Embed Size (px)
Citation preview
AngularJS 2.0 Forms
Eyal Vardi
Site: http://ng-course.org
Blog: eyalVardi.wordpres.com
Agenda Template Driven
Two-way data binding with ngModel Change tracking with ngControl ngForm
Model Driven Control & ControlGroup Form Builder
Validations Value Accessors
Form Directives (Template Driven)
Installation The forms classes exist in
'angular2/common'import { FORM_DIRECTIVES } from 'angular2/common';
ngFormngFormModel
ngModelngControl
ngFormControl
Controls (Model Driven)
Valid
ator
s
Acce
ssor
s
ControlControlGroupControlArray
My Component
(Model)name: string
Form Control’s Classes
ControlControlGroupControlArray
Component View Template
<input type="text" [(ngModel)] ="name" />
pristine dirty touched untouche
d errors valid
Data Flow
Template Driven
ngModel Directive Binds an existing domain model to a
form control.@Component({ selector: "search-comp", directives: [FORM_DIRECTIVES], template: `<input type='text' [(ngModel)]="name">`})class SearchComp { name: string;}
Inside [(ngModel)] 1/2
<input type="text" class="form-control"
required
[ngModel]="model.name"
(ngModelChange)="model.name =
$event" >
Name: {{model.name}}
Property Binding, a value flows from the model to a target property on screen.
Event Binding, we flow the value from the target property on screen to the model.
Inside [(ngModel)] 2/2@Directive({ selector: '[ngModel]:not([ngControl]):not([ngFormControl])', bindings: [formControlBinding], inputs : ['model: ngModel'], outputs : ['update: ngModelChange'], exportAs: 'ngForm'})export class NgModel extends NgControl implements OnChanges { _control = new Control(); /** @internal */ _added = false; /** @internal */ update = new EventEmitter(); model: any; viewModel: any;
constructor(...) {...} /** Properties & Methods */ }
ngControl Directive (NgControlName) Creates and binds a control with a
specified name to a DOM element.
<form #f="ngForm" (submit)='onLogIn(f.value)'>
Login <input type='text' ngControl='login'
#l="ngForm">
<div *ngIf="!l.valid">Login is invalid</div>
</form>
Component Field
Component Template Directive that in use
name : string
<input [(ngModel)]=“name” /> ngModel
name : string
<input ngControl=“dName” [(ngModel)]=“name” />
ngControlName
ngModel vs. ngControlName ngControlName directive selector
'[ngControl]'
ngModel directive selector '[ngModel]:not([ngControl]):not([ngFormControl])
'
This is still a ngControl
ngControl Properties Properties:
formDirective control value path validator asyncValidator
States Properties: pristine dirty touched untouched errors valid
Abstract class
Track change-state and validity
State Class if true Class if false
Control has been visited ng-touched ng-untouched
Control's value has changed ng-dirty ng-pristine
Control's value is valid ng-valid ng-invalid
NgControlStatus Directive Sets CSS classes based on ngControl
directive status (valid/invalid/dirty/etc).@Directive({ selector: '[ngControl],[ngModel],[ngFormControl]', host: { '[class.ng-untouched]': 'ngClassUntouched', '[class.ng-touched]' : 'ngClassTouched', '[class.ng-pristine]' : 'ngClassPristine', '[class.ng-dirty]' : 'ngClassDirty', '[class.ng-valid]' : 'ngClassValid', '[class.ng-invalid]' : 'ngClassInvalid' }})export class NgControlStatus { private _cd: NgControl; constructor(@Self() cd: NgControl) { this._cd = cd; } ...
get ngClassValid(): boolean { return isPresent(this._cd.control) ? this._cd.control.valid : false; } ...}
Data BindingTrack change-state
and validity
Validators
Control
My Component (model)
ngModel Directives ngControlName
ngFormControl
ngModelselector :
'[ngModel]:not([ngControl]):not([ngFormControl])',Inputs : ['model: ngModel'],
outputs : ['update: ngModelChange'],
selector : '[ngFormControl]'inputs : ['form: ngFormControl', 'model: ngModel'],outputs : ['update: ngModelChange'],
selector : '[ngControl]',inputs : ['name: ngControl', 'model: ngModel'],outputs : ['update: ngModelChange'],
Form Tag & NgForm Directive NgForm will get automatically attached to
any <form> tags you have in your view, when we used FORM_DIRECTIVES. A ControlGroup named ngForm A (ngSubmit) output
<form #f="ngForm" (ngSubmit)="onSubmit(f.value)"></form>
ExportAs
Output
Form Directives
Use on input tagUse on form tag
Errors Messages Form message
Field message
Specific validation
<div *ngIf="!myForm.valid" class="ui error message">Form is invalid
</div>
<div *ngIf="!name.valid" class="ui message" [class.error]="!name.valid && name.touched" >
Name is invalid</div>
<div *ngIf="name.hasError('required')" class="ui error message">Name is required</div>
Model Driven
Form Controls `Control`s have values and validation
state
AbstractControl Class Events:
valueChanges statusChanges
Methods: setErrors,getErrors & hasErrors find setParent markAsDirty,markAsXXX runValidator
Properties: value status valid errors pristine dirty touched untouched
Model Driven Forms A Control represents a single input field Controls encapsulate the field's value,
and states such as if it is valid, dirty (changed), or has errors.
Directives Controls
ngFormModel ControlGroup | ControlArray
ngFormControl Control
ngFormModel & ngFormControl ngFormModel Binds an existing control
group to a DOM element. ngFormControl Binds an existing Control
to a DOM element.
export class App { constructor() { this.myForm = new ControlGroup({ myControl: new Control("") }); }}
<form [ngFormModel]="myForm"> <input ngFormControl="myControl"></form>
FormBuilder Class Creates a form object from a user-
specified configuration. control(value: Object, validator?: Function, asyncValidator?: Function) :
Control group(controls: {[key: string]: any}, extra?: {[key: string]: any}) :
ControlGroup array(controls: any[], validator?: Function, asyncValidator?: Function) :
ControlArrayconstructor(builder: FormBuilder) {
this.loginForm = builder.group({
login: ["", Validators.required],
passwordRetry: builder.group({
password: ["", Validators.required],
pConfirmation: ["", Validators.required]
})
});
}
FormBuilder Class Creates a form object from a user-
specified configuration.@Component({ selector: 'my-app', viewBindings: [FORM_BINDINGS], template: ` <form [ngFormModel]="loginForm"> <p>Login <input ngFormControl="login"></p> <div ngControlGroup="passwordRetry"> <p>Password <input type="password" ngFormControl="password"></p> <p>Confirm password <input type="password" ngFormControl="pConfirma"></p> </div> </form> <h3>Form value:</h3> <pre>{{value}}</pre> `, directives: [FORM_DIRECTIVES]})
export class App { loginForm: ControlGroup; constructor(builder: FormBuilder) { this.loginForm = builder.group({ login: ["", Validators.required], passwordRetry: builder.group({ password: ["", Validators.required], pConfirm: ["", Validators.required] }) }); } get value(): string { return JSON.stringify( this.loginForm.value, null, 2); }}
Watching For Changes Both ControlGroup and Control have an
EventEmitter that we can use to observe changes.this.name.valueChanges.subscribe( (value: string) => { console.log('name changed to: ', value); });
this.myForm.valueChanges.subscribe( (value: string) => { console.log('form changed to: ', value); });
Validations
Built-in Validators Required MaxLength & MinLength
Custom Validator Directive@Directive({ selector: ' [my-validator][ngControl],
[my-validator][ngFormControl], [my-validator][ngModel]', providers: [provide( NG_VALIDATORS, { useExisting: myValidator, multi: true})]})export class myValidator implements Validator { private _validator: Function;
constructor(@Attribute("my-validator") myValidator: string) { this._validator = function(value){ return { "myValidator": true }; } }
validate(c: Control): {[key: string]: any} { return this._validator(c); }}
Returns a StringMap<string, boolean> where the key is "error code" and the value is true if it fails
Custom Validator Assigning the Validator to the Control:
this.myForm = fb.group({ 'name': [
'', Validators.compose([
Validators.required , nameValidator
])]
});
and operation
Value Accessors
Interface ControlValueAccessor A bridge between a control and a native
element. A ControlValueAccessor abstracts the
operations of writing a new value to a DOM element representing an input control.
DefaultValueAccessor Directive The default accessor for writing a value and
listening to changes that is used by the: NgModel NgFormControl NgControlName
InputInput ModelDefaultValueAccessor Renderer On Change
ngModel | ngFormControl | ngControlName
DVA Code@Directive({ selector: 'input:not([type=checkbox])[ngControl],
textarea[ngControl],input:not([type=checkbox])[ngFormControl],textarea[ngFormControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]',
host: { '(input)': 'onChange($event.target.value)', '(blur)' : 'onTouched()'},
bindings: [DEFAULT_VALUE_ACCESSOR]}) export class DefaultValueAccessor implements ControlValueAccessor { onChange = (_) => {}; onTouched = () => {};
constructor(private _renderer:Renderer, private _elementRef:ElementRef) {}
writeValue(value: any): void { var normalizedValue = isBlank(value) ? '' : value; this._renderer.setElementProperty(
this._elementRef, 'value', normalizedValue); } registerOnChange(fn: (_: any) => void): void { this.onChange = fn; } registerOnTouched(fn: () => void): void { this.onTouched = fn; }}
setUpControl function This function are call when the
ngOnChanges in ngModel directive is call in the first time.
export function setUpControl( control: Control , dir: NgControl ): void {
...
dir.valueAccessor.writeValue(control.value);
// view -> model dir.valueAccessor.registerOnChange(newValue => { dir.viewToModelUpdate(newValue); control.updateValue(newValue, {emitModelToViewChange: false}); control.markAsDirty(); });
// model -> view control.registerOnChange(newValue => dir.valueAccessor.writeValue(newValue));
// touched dir.valueAccessor.registerOnTouched(() => control.markAsTouched());}
Form Classes
Resources Angular.io site (Developer guides - forms) Ng-Book 2 (
The Ultimate Guide to Forms in Angular 2 ) GitHub (Angular code source)
Thankseyalvardi.wordpress.com
Eyal Vardi
Site: http://ng-course.org
Blog: eyalVardi.wordpres.com