import { FocusMonitor } from '@angular/cdk/a11y' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { Component , ElementRef , Input , OnDestroy } from '@angular/core' ; import { FormBuilder , FormGroup } from '@angular/forms' ; import { MatFormFieldControl } from '@angular/material' ; import { Subject } from 'rxjs' ; title @ Component ( { selector : 'form-field-custom-control-example' , templateUrl : 'form-field-custom-control-example.html' , styleUrls : [ 'form-field-custom-control-example.css' ] , } ) export class FormFieldCustomControlExample { } /** Data structure for holding telephone number. */ export class MyTel { constructor ( public area , public exchange , public subscriber ) { } } /** Custom `MatFormFieldControl` for telephone number input. */ @ Component ( { selector : 'example-tel-input' , templateUrl : 'example-tel-input-example.html' , styleUrls : [ 'example-tel-input-example.css' ] , providers : [ { provide : MatFormFieldControl , useExisting : MyTelInput } ] , host : { '[class.example-floating]' : 'shouldLabelFloat' , '[id]' : 'id' , '[attr.aria-describedby]' : 'describedBy' , } } ) export class MyTelInput implements MatFormFieldControl < MyTel > , OnDestroy { static nextId = 0 ; parts : FormGroup ; stateChanges = new Subject < void > ( ) ; focused = false ; ngControl = null ; errorState = false ; controlType = 'example-tel-input' ; id = `example-tel-input-${ MyTelInput . nextId ++ }` ; describedBy = '' ; get empty ( ) { const { value : { area , exchange , subscriber } } = this . parts ; return ! area && ! exchange && ! subscriber ; } get shouldLabelFloat ( ) { return this . focused || ! this . empty ; } @ Input ( ) get placeholder ( ) : string { return this . _placeholder ; } set placeholder ( value ) { this . _placeholder = value ; this . stateChanges . next ( ) ; } private _placeholder : string ; @ Input ( ) get required ( ) : boolean { return this . _required ; } set required ( value ) { this . _required = coerceBooleanProperty ( value ) ; this . stateChanges . next ( ) ; } private _required = false ; @ Input ( ) get disabled ( ) : boolean { return this . _disabled ; } set disabled ( value ) { this . _disabled = coerceBooleanProperty ( value ) ; this . stateChanges . next ( ) ; } private _disabled = false ; @ Input ( ) get value ( ) : MyTel | null { const { value : { area , exchange , subscriber } } = this . parts ; if ( area . length === 3 && exchange . length === 3 && subscriber . length === 4 ) { return new MyTel ( area , exchange , subscriber ) ; } return null ; } set value ( tel ) { const { area , exchange , subscriber } = tel || new MyTel ( '' , '' , '' ) ; this . parts . setValue ( { area , exchange , subscriber } ) ; this . stateChanges . next ( ) ; } constructor ( fb , private fm , private elRef ) { this . parts = fb . group ( { area : '' , exchange : '' , subscriber : '' , } ) ; fm . monitor ( elRef , true ) . subscribe ( origin => { this . focused = ! ! origin ; this . stateChanges . next ( ) ; } ) ; } ngOnDestroy ( ) { this . stateChanges . complete ( ) ; this . fm . stopMonitoring ( this . elRef ) ; } setDescribedByIds ( ids ) { this . describedBy = ids . join ( ' ' ) ; } onContainerClick ( event ) { if ( ( event . target as Element ) . tagName . toLowerCase ( ) != 'input' ) { this . elRef . nativeElement . querySelector ( 'input' ) ! . focus ( ) ; } } }