1
1
import { FocusMonitor } from '@angular/cdk/a11y' ;
2
2
import { 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' ;
5
21
import { MatFormFieldControl } from '@angular/material/form-field' ;
6
22
import { Subject } from 'rxjs' ;
7
23
8
24
/** @title Form field with custom telephone number input control. */
9
25
@Component ( {
10
26
selector : 'form-field-custom-control-example' ,
11
27
templateUrl : 'form-field-custom-control-example.html' ,
12
- styleUrls : [ 'form-field-custom-control-example.css' ] ,
28
+ styleUrls : [ 'form-field-custom-control-example.css' ]
13
29
} )
14
- export class FormFieldCustomControlExample { }
30
+ export class FormFieldCustomControlExample {
31
+ form : FormGroup = new FormGroup ( {
32
+ tel : new FormControl ( new MyTel ( '' , '' , '' ) )
33
+ } ) ;
34
+ }
15
35
16
36
/** Data structure for holding telephone number. */
17
37
export 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
+ ) { }
19
43
}
20
44
21
45
/** Custom `MatFormFieldControl` for telephone number input. */
22
46
@Component ( {
23
47
selector : 'example-tel-input' ,
24
48
templateUrl : 'example-tel-input-example.html' ,
25
49
styleUrls : [ 'example-tel-input-example.css' ] ,
26
- providers : [ { provide : MatFormFieldControl , useExisting : MyTelInput } ] ,
50
+ providers : [ { provide : MatFormFieldControl , useExisting : MyTelInput } ] ,
27
51
host : {
28
52
'[class.example-floating]' : 'shouldLabelFloat' ,
29
53
'[id]' : 'id' ,
30
- '[attr.aria-describedby]' : 'describedBy' ,
54
+ '[attr.aria-describedby]' : 'describedBy'
31
55
}
32
56
} )
33
- export class MyTelInput implements ControlValueAccessor , MatFormFieldControl < MyTel > , OnDestroy {
57
+ export class MyTelInput
58
+ implements ControlValueAccessor , MatFormFieldControl < MyTel > , OnDestroy {
34
59
static nextId = 0 ;
60
+ @ViewChild ( 'area' ) areaInput : HTMLInputElement ;
61
+ @ViewChild ( 'exchange' ) exchangeInput : HTMLInputElement ;
62
+ @ViewChild ( 'subscriber' ) subscriberInput : HTMLInputElement ;
35
63
36
64
parts : FormGroup ;
37
65
stateChanges = new Subject < void > ( ) ;
@@ -44,31 +72,41 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
44
72
onTouched = ( ) => { } ;
45
73
46
74
get empty ( ) {
47
- const { value : { area, exchange, subscriber} } = this . parts ;
75
+ const {
76
+ value : { area, exchange, subscriber }
77
+ } = this . parts ;
48
78
49
79
return ! area && ! exchange && ! subscriber ;
50
80
}
51
81
52
- get shouldLabelFloat ( ) { return this . focused || ! this . empty ; }
82
+ get shouldLabelFloat ( ) {
83
+ return this . focused || ! this . empty ;
84
+ }
53
85
54
86
@Input ( )
55
- get placeholder ( ) : string { return this . _placeholder ; }
87
+ get placeholder ( ) : string {
88
+ return this . _placeholder ;
89
+ }
56
90
set placeholder ( value : string ) {
57
91
this . _placeholder = value ;
58
92
this . stateChanges . next ( ) ;
59
93
}
60
94
private _placeholder : string ;
61
95
62
96
@Input ( )
63
- get required ( ) : boolean { return this . _required ; }
97
+ get required ( ) : boolean {
98
+ return this . _required ;
99
+ }
64
100
set required ( value : boolean ) {
65
101
this . _required = coerceBooleanProperty ( value ) ;
66
102
this . stateChanges . next ( ) ;
67
103
}
68
104
private _required = false ;
69
105
70
106
@Input ( )
71
- get disabled ( ) : boolean { return this . _disabled ; }
107
+ get disabled ( ) : boolean {
108
+ return this . _disabled ;
109
+ }
72
110
set disabled ( value : boolean ) {
73
111
this . _disabled = coerceBooleanProperty ( value ) ;
74
112
this . _disabled ? this . parts . disable ( ) : this . parts . enable ( ) ;
@@ -79,27 +117,38 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
79
117
@Input ( )
80
118
get value ( ) : MyTel | null {
81
119
if ( this . parts . valid ) {
82
- const { value : { area, exchange, subscriber} } = this . parts ;
120
+ const {
121
+ value : { area, exchange, subscriber }
122
+ } = this . parts ;
83
123
return new MyTel ( area , exchange , subscriber ) ;
84
124
}
85
125
return null ;
86
126
}
87
127
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 } ) ;
90
130
this . stateChanges . next ( ) ;
91
131
}
92
132
93
133
constructor (
94
134
formBuilder : FormBuilder ,
95
135
private _focusMonitor : FocusMonitor ,
96
136
private _elementRef : ElementRef < HTMLElement > ,
97
- @Optional ( ) @Self ( ) public ngControl : NgControl ) {
98
-
137
+ @Optional ( ) @Self ( ) public ngControl : NgControl
138
+ ) {
99
139
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
+ ]
103
152
} ) ;
104
153
105
154
_focusMonitor . monitor ( _elementRef , true ) . subscribe ( origin => {
@@ -115,6 +164,18 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
115
164
}
116
165
}
117
166
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
+
118
179
ngOnDestroy ( ) {
119
180
this . stateChanges . complete ( ) ;
120
181
this . _focusMonitor . stopMonitoring ( this . _elementRef ) ;
@@ -125,8 +186,14 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
125
186
}
126
187
127
188
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' ) ;
130
197
}
131
198
}
132
199
@@ -146,7 +213,8 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
146
213
this . disabled = isDisabled ;
147
214
}
148
215
149
- _handleInput ( ) : void {
216
+ _handleInput ( control : AbstractControl , nextElement ?: HTMLInputElement ) : void {
217
+ this . autoFocusNext ( control , nextElement ) ;
150
218
this . onChange ( this . value ) ;
151
219
}
152
220
0 commit comments