11import { FocusMonitor } from '@angular/cdk/a11y' ;
22import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
3- import { Component , ElementRef , Input , OnDestroy , Optional , Self } from '@angular/core' ;
4- import { FormBuilder , FormGroup , ControlValueAccessor , NgControl , Validators } from '@angular/forms' ;
3+ import {
4+ Component ,
5+ ElementRef ,
6+ Input ,
7+ OnDestroy ,
8+ Optional ,
9+ Self ,
10+ ViewChild
11+ } from '@angular/core' ;
12+ import {
13+ FormBuilder ,
14+ FormGroup ,
15+ ControlValueAccessor ,
16+ NgControl ,
17+ Validators ,
18+ FormControl ,
19+ AbstractControl
20+ } from '@angular/forms' ;
521import { MatFormFieldControl } from '@angular/material/form-field' ;
622import { Subject } from 'rxjs' ;
723
824/** @title Form field with custom telephone number input control. */
925@Component ( {
1026 selector : 'form-field-custom-control-example' ,
1127 templateUrl : 'form-field-custom-control-example.html' ,
12- styleUrls : [ 'form-field-custom-control-example.css' ] ,
28+ styleUrls : [ 'form-field-custom-control-example.css' ]
1329} )
14- export class FormFieldCustomControlExample { }
30+ export class FormFieldCustomControlExample {
31+ form : FormGroup = new FormGroup ( {
32+ tel : new FormControl ( new MyTel ( '' , '' , '' ) )
33+ } ) ;
34+ }
1535
1636/** Data structure for holding telephone number. */
1737export class MyTel {
18- constructor ( public area : string , public exchange : string , public subscriber : string ) { }
38+ constructor (
39+ public area : string ,
40+ public exchange : string ,
41+ public subscriber : string
42+ ) { }
1943}
2044
2145/** Custom `MatFormFieldControl` for telephone number input. */
2246@Component ( {
2347 selector : 'example-tel-input' ,
2448 templateUrl : 'example-tel-input-example.html' ,
2549 styleUrls : [ 'example-tel-input-example.css' ] ,
26- providers : [ { provide : MatFormFieldControl , useExisting : MyTelInput } ] ,
50+ providers : [ { provide : MatFormFieldControl , useExisting : MyTelInput } ] ,
2751 host : {
2852 '[class.example-floating]' : 'shouldLabelFloat' ,
2953 '[id]' : 'id' ,
30- '[attr.aria-describedby]' : 'describedBy' ,
54+ '[attr.aria-describedby]' : 'describedBy'
3155 }
3256} )
33- export class MyTelInput implements ControlValueAccessor , MatFormFieldControl < MyTel > , OnDestroy {
57+ export class MyTelInput
58+ implements ControlValueAccessor , MatFormFieldControl < MyTel > , OnDestroy {
3459 static nextId = 0 ;
60+ @ViewChild ( 'area' ) areaInput : HTMLInputElement ;
61+ @ViewChild ( 'exchange' ) exchangeInput : HTMLInputElement ;
62+ @ViewChild ( 'subscriber' ) subscriberInput : HTMLInputElement ;
3563
3664 parts : FormGroup ;
3765 stateChanges = new Subject < void > ( ) ;
@@ -44,31 +72,41 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
4472 onTouched = ( ) => { } ;
4573
4674 get empty ( ) {
47- const { value : { area, exchange, subscriber} } = this . parts ;
75+ const {
76+ value : { area, exchange, subscriber }
77+ } = this . parts ;
4878
4979 return ! area && ! exchange && ! subscriber ;
5080 }
5181
52- get shouldLabelFloat ( ) { return this . focused || ! this . empty ; }
82+ get shouldLabelFloat ( ) {
83+ return this . focused || ! this . empty ;
84+ }
5385
5486 @Input ( )
55- get placeholder ( ) : string { return this . _placeholder ; }
87+ get placeholder ( ) : string {
88+ return this . _placeholder ;
89+ }
5690 set placeholder ( value : string ) {
5791 this . _placeholder = value ;
5892 this . stateChanges . next ( ) ;
5993 }
6094 private _placeholder : string ;
6195
6296 @Input ( )
63- get required ( ) : boolean { return this . _required ; }
97+ get required ( ) : boolean {
98+ return this . _required ;
99+ }
64100 set required ( value : boolean ) {
65101 this . _required = coerceBooleanProperty ( value ) ;
66102 this . stateChanges . next ( ) ;
67103 }
68104 private _required = false ;
69105
70106 @Input ( )
71- get disabled ( ) : boolean { return this . _disabled ; }
107+ get disabled ( ) : boolean {
108+ return this . _disabled ;
109+ }
72110 set disabled ( value : boolean ) {
73111 this . _disabled = coerceBooleanProperty ( value ) ;
74112 this . _disabled ? this . parts . disable ( ) : this . parts . enable ( ) ;
@@ -79,27 +117,38 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
79117 @Input ( )
80118 get value ( ) : MyTel | null {
81119 if ( this . parts . valid ) {
82- const { value : { area, exchange, subscriber} } = this . parts ;
120+ const {
121+ value : { area, exchange, subscriber }
122+ } = this . parts ;
83123 return new MyTel ( area , exchange , subscriber ) ;
84124 }
85125 return null ;
86126 }
87127 set value ( tel : MyTel | null ) {
88- const { area, exchange, subscriber} = tel || new MyTel ( '' , '' , '' ) ;
89- this . parts . setValue ( { area, exchange, subscriber} ) ;
128+ const { area, exchange, subscriber } = tel || new MyTel ( '' , '' , '' ) ;
129+ this . parts . setValue ( { area, exchange, subscriber } ) ;
90130 this . stateChanges . next ( ) ;
91131 }
92132
93133 constructor (
94134 formBuilder : FormBuilder ,
95135 private _focusMonitor : FocusMonitor ,
96136 private _elementRef : ElementRef < HTMLElement > ,
97- @Optional ( ) @Self ( ) public ngControl : NgControl ) {
98-
137+ @Optional ( ) @Self ( ) public ngControl : NgControl
138+ ) {
99139 this . parts = formBuilder . group ( {
100- area : [ null , [ Validators . required , Validators . minLength ( 3 ) , Validators . maxLength ( 3 ) ] ] ,
101- exchange : [ null , [ Validators . required , Validators . minLength ( 3 ) , Validators . maxLength ( 3 ) ] ] ,
102- subscriber : [ null , [ Validators . required , Validators . minLength ( 4 ) , Validators . maxLength ( 4 ) ] ] ,
140+ area : [
141+ null ,
142+ [ Validators . required , Validators . minLength ( 3 ) , Validators . maxLength ( 3 ) ]
143+ ] ,
144+ exchange : [
145+ null ,
146+ [ Validators . required , Validators . minLength ( 3 ) , Validators . maxLength ( 3 ) ]
147+ ] ,
148+ subscriber : [
149+ null ,
150+ [ Validators . required , Validators . minLength ( 4 ) , Validators . maxLength ( 4 ) ]
151+ ]
103152 } ) ;
104153
105154 _focusMonitor . monitor ( _elementRef , true ) . subscribe ( origin => {
@@ -115,6 +164,18 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
115164 }
116165 }
117166
167+ autoFocusNext ( control : AbstractControl , nextElement ?: HTMLInputElement ) : void {
168+ if ( ! control . errors && nextElement ) {
169+ this . _focusMonitor . focusVia ( nextElement , 'program' ) ;
170+ }
171+ }
172+
173+ autoFocusPrev ( control : AbstractControl , prevElement : HTMLInputElement ) : void {
174+ if ( control . value . length < 1 ) {
175+ this . _focusMonitor . focusVia ( prevElement , 'program' ) ;
176+ }
177+ }
178+
118179 ngOnDestroy ( ) {
119180 this . stateChanges . complete ( ) ;
120181 this . _focusMonitor . stopMonitoring ( this . _elementRef ) ;
@@ -125,8 +186,14 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
125186 }
126187
127188 onContainerClick ( event : MouseEvent ) {
128- if ( ( event . target as Element ) . tagName . toLowerCase ( ) != 'input' ) {
129- this . _elementRef . nativeElement . querySelector ( 'input' ) ! . focus ( ) ;
189+ if ( this . parts . controls . subscriber . valid ) {
190+ this . _focusMonitor . focusVia ( this . subscriberInput , 'program' ) ;
191+ } else if ( this . parts . controls . exchange . valid ) {
192+ this . _focusMonitor . focusVia ( this . subscriberInput , 'program' ) ;
193+ } else if ( this . parts . controls . area . valid ) {
194+ this . _focusMonitor . focusVia ( this . exchangeInput , 'program' ) ;
195+ } else {
196+ this . _focusMonitor . focusVia ( this . areaInput , 'program' ) ;
130197 }
131198 }
132199
@@ -146,7 +213,8 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
146213 this . disabled = isDisabled ;
147214 }
148215
149- _handleInput ( ) : void {
216+ _handleInput ( control : AbstractControl , nextElement ?: HTMLInputElement ) : void {
217+ this . autoFocusNext ( control , nextElement ) ;
150218 this . onChange ( this . value ) ;
151219 }
152220
0 commit comments