import { AfterViewInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , ElementRef , EventEmitter , Inject , InjectionToken , Input , OnDestroy , Optional , Output , ViewChild , ViewEncapsulation } from '@angular/core' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { UniqueSelectionDispatcher } from '@angular/cdk/collections' ; import { supportsPassiveEventListeners } from '@angular/cdk/platform' ; import { MDCRippleFoundation , MDCRippleAdapter } from '@material/ripple' ; import { MDCRadioFoundation , MDCRadioAdapter } from '@material/radio' ; import { MdcRipple , MDCRippleCapableSurface } from '@angular-mdc/web/ripple' ; import { MdcFormField , MdcFormFieldControl } from '@angular-mdc/web/form-field' ; import { MDCComponent } from '@angular-mdc/web/base' ; /**
 * Describes a parent MdcRadioGroup component.
 * Contains properties that MdcRadio can inherit.
 */ export interface MdcRadioGroupParentComponent { name : string ; selected : MdcRadio | null ; value : any ; disabled : boolean ; required : boolean ; emitChangeEvent ( ) : void ; _controlValueAccessorChangeFn ( value ) : void ; _touch ( ) : void ; } /**
 * Injection token used to provide the parent MdcRadioGroup component to MdcRadio.
 */ export const MDC_RADIO_GROUP_PARENT_COMPONENT = new InjectionToken < MdcRadioGroupParentComponent > ( 'MDC_RADIO_GROUP_PARENT_COMPONENT' ) ; /** Change event object emitted by MdcRadio. */ export class MdcRadioChange { constructor ( /** The source MdcRadio of the event. */ public source , /** The value of the radio button. */ public value ) { } } let nextUniqueId = 0 ; @ Component ( { selector : 'mdc-radio' , exportAs : 'mdcRadio' , host : { '[id]' : 'id' , 'class' : 'mdc-radio' , '(focus)' : 'input.nativeElement.focus()' , '[attr.tabindex]' : '-1' , '[attr.name]' : 'null' , '[class.mdc-radio--touch]' : 'touch' , } , templateUrl : 'radio.html' , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush , providers : [ MdcRipple , { provide : MdcFormFieldControl , useExisting : MdcRadio } ] } ) export class MdcRadio extends MDCComponent < MDCRadioFoundation > implements AfterViewInit , OnDestroy , MdcFormFieldControl < any > , MDCRippleCapableSurface { private _uniqueId : string = `mdc-radio-${ ++ nextUniqueId }` ; private _initialized : boolean = false ; _root ! : Element ; /** The unique ID for the radio button. */ @ Input ( ) id : string = this . _uniqueId ; /** Analog to HTML 'name' attribute used to group radios for unique selection. */ @ Input ( ) name ! : string ; @ Input ( ) tabIndex : number = 0 ; @ Input ( 'aria-label' ) ariaLabel ? : string ; @ Input ( 'aria-labelledby' ) ariaLabelledby ? : string ; /** The 'aria-describedby' attribute is read after the element's label and field type. */ @ Input ( 'aria-describedby' ) ariaDescribedby ? : string ; get inputId ( ) : string { return `${ this . id || this . _uniqueId }-input` ; } @ Input ( ) get touch ( ) : boolean { return this . _touch ; } set touch ( value ) { this . _touch = coerceBooleanProperty ( value ) ; } private _touch : boolean = false ; @ Input ( ) get value ( ) : any { return this . _value ; } set value ( newValue ) { this . setValue ( newValue ) ; } private _value : any ; @ Input ( ) get checked ( ) : boolean { return this . _checked ; } set checked ( value ) { this . setChecked ( value ) ; } private _checked : boolean = false ; @ Input ( ) get disabled ( ) : boolean { return this . _disabled || ( this . radioGroup !== null && this . radioGroup . disabled ) ; } set disabled ( value ) { const newDisabledState = coerceBooleanProperty ( value ) ; if ( this . _disabled !== newDisabledState ) { this . _disabled = newDisabledState ; this . _foundation . setDisabled ( this . _disabled ) ; this . _changeDetectorRef . markForCheck ( ) ; } } private _disabled : boolean = false ; @ Input ( ) get required ( ) : boolean { return this . _required || ( this . radioGroup && this . radioGroup . required ) ; } set required ( value ) { this . _required = coerceBooleanProperty ( value ) ; } private _required : boolean = false ; @ Output ( ) readonly change : EventEmitter < MdcRadioChange > = new EventEmitter < MdcRadioChange > ( ) ; @ ViewChild ( 'input' , { static : true } ) input ! : ElementRef < HTMLInputElement > ; /** Unregister function for _radioDispatcher */ private _removeUniqueSelectionListener : ( ) => void = ( ) => { } ; getDefaultFoundation ( ) { const adapter = { addClass : ( className ) => this . _root . classList . add ( className ) , removeClass : ( className ) => this . _root . classList . remove ( className ) , setNativeControlDisabled : ( disabled ) => this . disabled = disabled } ; return new MDCRadioFoundation ( adapter ) ; } constructor ( private _changeDetectorRef , public elementRef , public ripple , private _radioDispatcher , @ Optional ( ) @ Inject ( MDC_RADIO_GROUP_PARENT_COMPONENT ) public radioGroup , @ Optional ( ) private _parentFormField ) { super ( elementRef ) ; if ( this . _parentFormField ) { _parentFormField . elementRef . nativeElement . classList . add ( 'mdc-form-field' ) ; } this . _root = elementRef . nativeElement ; this . ripple = this . _createRipple ( ) ; this . _removeUniqueSelectionListener = _radioDispatcher . listen ( ( id , name ) => { if ( id !== this . id && name === this . name ) { this . checked = this . input . nativeElement . checked ; } } ) ; } ngAfterViewInit ( ) { this . _initialized = true ; this . _foundation . init ( ) ; if ( this . radioGroup ) { Promise . resolve ( ) . then ( ( ) => { this . checked = this . radioGroup . value === this . _value ; this . name = this . radioGroup . name ; this . setChecked ( this . checked ) ; this . _changeDetectorRef . markForCheck ( ) ; } ) ; } } ngOnDestroy ( ) { this . _removeUniqueSelectionListener ( ) ; this . ripple . destroy ( ) ; this . _foundation . destroy ( ) ; } onInputClick ( event ) { event . stopPropagation ( ) ; } onInputChange ( event ) { this . ripple . init ( ) ; event . stopPropagation ( ) ; const groupValueChanged = this . radioGroup && this . value !== this . radioGroup . value ; this . checked = true ; this . _emitChangeEvent ( ) ; if ( this . radioGroup ) { this . radioGroup . _controlValueAccessorChangeFn ( this . value ) ; if ( groupValueChanged ) { this . radioGroup . emitChangeEvent ( ) ; } } } setChecked ( checked ) { if ( ! this . _initialized ) { return ; } const newCheckedState = coerceBooleanProperty ( checked ) ; if ( this . _checked !== newCheckedState ) { this . _checked = newCheckedState ; this . input . nativeElement . checked = newCheckedState ; if ( newCheckedState && this . radioGroup && this . radioGroup . value !== this . value ) { this . radioGroup . selected = this ; } else if ( ! newCheckedState && this . radioGroup && this . radioGroup . value === this . value ) { this . radioGroup . selected = null ; } this . _changeDetectorRef . markForCheck ( ) ; if ( newCheckedState ) { this . _radioDispatcher . notify ( this . id , this . name ) ; } } } setValue ( value ) { this . _value = value ; this . input . nativeElement . value = this . _value ; if ( this . radioGroup !== null ) { if ( ! this . checked ) { this . checked = this . radioGroup . value === value ; } if ( this . checked ) { this . radioGroup . selected = this ; } } } focus ( ) { this . input . nativeElement . focus ( ) ; } markForCheck ( ) { this . _changeDetectorRef . markForCheck ( ) ; } private _createRipple ( ) { const adapter = { ... MdcRipple . createAdapter ( this ) , isSurfaceActive : ( ) => false , isUnbounded : ( ) => true , deregisterInteractionHandler : ( evtType , handler ) => this . input . nativeElement . removeEventListener ( evtType , handler , supportsPassiveEventListeners ( ) ) , registerInteractionHandler : ( evtType , handler ) => this . input . nativeElement . addEventListener ( evtType , handler , supportsPassiveEventListeners ( ) ) , } ; return new MdcRipple ( this . elementRef , new MDCRippleFoundation ( adapter ) ) ; } /** Dispatch change event with current value. */ private _emitChangeEvent ( ) { this . change . emit ( new MdcRadioChange ( this , this . _value ) ) ; } }