license import { FocusMonitor } from '@angular/cdk/a11y' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { UniqueSelectionDispatcher } from '@angular/cdk/collections' ; import { AfterContentInit , AfterViewInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , ContentChildren , Directive , ElementRef , EventEmitter , forwardRef , Inject , Input , OnDestroy , OnInit , Optional , Output , QueryList , ViewChild , ViewEncapsulation , } from '@angular/core' ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ; import { CanColor , CanColorCtor , CanDisable , CanDisableCtor , CanDisableRipple , CanDisableRippleCtor , HasTabIndex , HasTabIndexCtor , mixinColor , mixinDisabled , mixinDisableRipple , mixinTabIndex , } from '@angular/material/core' ; import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations' ; let nextUniqueId = 0 ; docs-private export const MAT_RADIO_GROUP_CONTROL_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => MatRadioGroup ) , multi : true } ; /** Change event object emitted by MatRadio and MatRadioGroup. */ export class MatRadioChange { constructor ( /** The MatRadioButton that emits the change event. */ public source , /** The value of the MatRadioButton. */ public value ) { } } docs-private export class MatRadioGroupBase { } export const _MatRadioGroupMixinBase = mixinDisabled ( MatRadioGroupBase ) ; /**
 * A group of radio buttons. May contain one or more `<mat-radio-button>` elements.
 */ @ Directive ( { selector : 'mat-radio-group' , exportAs : 'matRadioGroup' , providers : [ MAT_RADIO_GROUP_CONTROL_VALUE_ACCESSOR ] , host : { 'role' : 'radiogroup' , 'class' : 'mat-radio-group' , } , inputs : [ 'disabled' ] , } ) export class MatRadioGroup extends _MatRadioGroupMixinBase implements AfterContentInit , ControlValueAccessor , CanDisable { /** Selected value for the radio group. */ private _value : any = null ; /** The HTML name attribute applied to radio buttons in this group. */ private _name : string = `mat-radio-group-${ nextUniqueId ++ }` ; /** The currently selected radio button. Should match value. */ private _selected : MatRadioButton | null = null ; /** Whether the `value` has been set to its initial value. */ private _isInitialized : boolean = false ; /** Whether the labels should appear after or before the radio-buttons. Defaults to 'after' */ private _labelPosition : 'before' | 'after' = 'after' ; /** Whether the radio group is disabled. */ private _disabled : boolean = false ; /** Whether the radio group is required. */ private _required : boolean = false ; /** The method to be called in order to update ngModel */ _controlValueAccessorChangeFn : ( value : any ) => void = ( ) => { } ; docs-private onTouched : ( ) => any = ( ) => { } ; /**
     * Event emitted when the group value changes.
     * Change events are only emitted when the value changes due to user interaction with
     * a radio button (the same behavior as `<input type-"radio">`).
     */ @ Output ( ) readonly change : EventEmitter < MatRadioChange > = new EventEmitter < MatRadioChange > ( ) ; /** Child radio buttons. */ @ ContentChildren ( forwardRef ( ( ) => MatRadioButton ) , { descendants : true } ) _radios : QueryList < MatRadioButton > ; /** Name of the radio button group. All radio buttons inside this group will use this name. */ @ Input ( ) get name ( ) : string { return this . _name ; } set name ( value ) { this . _name = value ; this . _updateRadioButtonNames ( ) ; } /** Whether the labels should appear after or before the radio-buttons. Defaults to 'after' */ @ Input ( ) get labelPosition ( ) : 'before' | 'after' { return this . _labelPosition ; } set labelPosition ( v ) { this . _labelPosition = v === 'before' ? 'before' : 'after' ; this . _markRadiosForCheck ( ) ; } /**
     * Value for the radio-group. Should equal the value of the selected radio button if there is
     * a corresponding radio button with a matching value. If there is not such a corresponding
     * radio button, this value persists to be applied in case a new radio button is added with a
     * matching value.
     */ @ Input ( ) get value ( ) : any { return this . _value ; } set value ( newValue ) { if ( this . _value !== newValue ) { this . _value = newValue ; this . _updateSelectedRadioFromValue ( ) ; this . _checkSelectedRadioButton ( ) ; } } _checkSelectedRadioButton ( ) { if ( this . _selected && ! this . _selected . checked ) { this . _selected . checked = true ; } } /**
     * The currently selected radio button. If set to a new radio button, the radio group value
     * will be updated to match the new selected button.
     */ @ Input ( ) get selected ( ) { return this . _selected ; } set selected ( selected ) { this . _selected = selected ; this . value = selected ? selected . value : null ; this . _checkSelectedRadioButton ( ) ; } /** Whether the radio group is disabled */ @ Input ( ) get disabled ( ) : boolean { return this . _disabled ; } set disabled ( value ) { this . _disabled = coerceBooleanProperty ( value ) ; this . _markRadiosForCheck ( ) ; } /** Whether the radio group is required */ @ Input ( ) get required ( ) : boolean { return this . _required ; } set required ( value ) { this . _required = coerceBooleanProperty ( value ) ; this . _markRadiosForCheck ( ) ; } constructor ( private _changeDetector ) { super ( ) ; } /**
     * Initialize properties once content children are available.
     * This allows us to propagate relevant attributes to associated buttons.
     */ ngAfterContentInit ( ) { this . _isInitialized = true ; } /**
     * Mark this group as being "touched" (for ngModel). Meant to be called by the contained
     * radio buttons upon their blur.
     */ _touch ( ) { if ( this . onTouched ) { this . onTouched ( ) ; } } private _updateRadioButtonNames ( ) { if ( this . _radios ) { this . _radios . forEach ( radio => { radio . name = this . name ; } ) ; } } /** Updates the `selected` radio button from the internal _value state. */ private _updateSelectedRadioFromValue ( ) { const isAlreadySelected = this . _selected !== null && this . _selected . value === this . _value ; if ( this . _radios && ! isAlreadySelected ) { this . _selected = null ; this . _radios . forEach ( radio => { radio . checked = this . value === radio . value ; if ( radio . checked ) { this . _selected = radio ; } } ) ; } } /** Dispatch change event with current selection and group value. */ _emitChangeEvent ( ) { if ( this . _isInitialized ) { this . change . emit ( new MatRadioChange ( this . _selected ! , this . _value ) ) ; } } _markRadiosForCheck ( ) { if ( this . _radios ) { this . _radios . forEach ( radio => radio . _markForCheck ( ) ) ; } } param value writeValue ( value ) { this . value = value ; this . _changeDetector . markForCheck ( ) ; } param fn registerOnChange ( fn ) { this . _controlValueAccessorChangeFn = fn ; } param fn registerOnTouched ( fn ) { this . onTouched = fn ; } param isDisabled setDisabledState ( isDisabled ) { this . disabled = isDisabled ; this . _changeDetector . markForCheck ( ) ; } } docs-private export class MatRadioButtonBase { disabled : boolean ; constructor ( public _elementRef ) { } } export const _MatRadioButtonMixinBase = mixinColor ( mixinDisableRipple ( mixinTabIndex ( MatRadioButtonBase ) ) , 'accent' ) ; /**
 * A Material design radio-button. Typically placed inside of `<mat-radio-group>` elements.
 */ @ Component ( { moduleId : module . id , selector : 'mat-radio-button' , templateUrl : 'radio.html' , styleUrls : [ 'radio.css' ] , inputs : [ 'color' , 'disableRipple' , 'tabIndex' ] , encapsulation : ViewEncapsulation . None , exportAs : 'matRadioButton' , host : { 'class' : 'mat-radio-button' , '[class.mat-radio-checked]' : 'checked' , '[class.mat-radio-disabled]' : 'disabled' , '[class._mat-animation-noopable]' : '_animationMode === "NoopAnimations"' , '[attr.tabindex]' : '-1' , '[attr.id]' : 'id' , '(focus)' : '_inputElement.nativeElement.focus()' , } , changeDetection : ChangeDetectionStrategy . OnPush , } ) export class MatRadioButton extends _MatRadioButtonMixinBase implements OnInit , AfterViewInit , OnDestroy , CanColor , CanDisableRipple , HasTabIndex { private _uniqueId : string = `mat-radio-${ ++ nextUniqueId }` ; /** 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 ; /** Used to set the 'aria-label' attribute on the underlying input element. */ @ Input ( 'aria-label' ) ariaLabel : string ; /** The 'aria-labelledby' attribute takes precedence as the element's text alternative. */ @ Input ( 'aria-labelledby' ) ariaLabelledby : string ; /** The 'aria-describedby' attribute is read after the element's label and field type. */ @ Input ( 'aria-describedby' ) ariaDescribedby : string ; /** Whether this radio button is checked. */ @ Input ( ) get checked ( ) : boolean { return this . _checked ; } set checked ( value ) { const newCheckedState = coerceBooleanProperty ( value ) ; if ( this . _checked !== newCheckedState ) { this . _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 ; } if ( newCheckedState ) { this . _radioDispatcher . notify ( this . id , this . name ) ; } this . _changeDetector . markForCheck ( ) ; } } /** The value of this radio button. */ @ Input ( ) get value ( ) : any { return this . _value ; } set value ( value ) { if ( this . _value !== value ) { this . _value = value ; if ( this . radioGroup !== null ) { if ( ! this . checked ) { this . checked = this . radioGroup . value === value ; } if ( this . checked ) { this . radioGroup . selected = this ; } } } } /** Whether the label should appear after or before the radio button. Defaults to 'after' */ @ Input ( ) get labelPosition ( ) : 'before' | 'after' { return this . _labelPosition || ( this . radioGroup && this . radioGroup . labelPosition ) || 'after' ; } set labelPosition ( value ) { this . _labelPosition = value ; } private _labelPosition : 'before' | 'after' ; /** Whether the radio button is disabled. */ @ 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 . _changeDetector . markForCheck ( ) ; } } /** Whether the radio button is required. */ @ Input ( ) get required ( ) : boolean { return this . _required || ( this . radioGroup && this . radioGroup . required ) ; } set required ( value ) { this . _required = coerceBooleanProperty ( value ) ; } /**
     * Event emitted when the checked state of this radio button changes.
     * Change events are only emitted when the value changes due to user interaction with
     * the radio button (the same behavior as `<input type-"radio">`).
     */ @ Output ( ) readonly change : EventEmitter < MatRadioChange > = new EventEmitter < MatRadioChange > ( ) ; /** The parent radio group. May or may not be present. */ radioGroup : MatRadioGroup ; /** ID of the native input element inside `<mat-radio-button>` */ get inputId ( ) : string { return `${ this . id || this . _uniqueId }-input` ; } /** Whether this radio is checked. */ private _checked : boolean = false ; /** Whether this radio is disabled. */ private _disabled : boolean ; /** Whether this radio is required. */ private _required : boolean ; /** Value assigned to this radio. */ private _value : any = null ; /** Unregister function for _radioDispatcher */ private _removeUniqueSelectionListener : ( ) => void = ( ) => { } ; /** The native `<input type=radio>` element */ @ ViewChild ( 'input' ) _inputElement : ElementRef < HTMLInputElement > ; constructor ( @ Optional ( ) radioGroup , elementRef , private _changeDetector , private _focusMonitor , private _radioDispatcher , @ Optional ( ) @ Inject ( ANIMATION_MODULE_TYPE ) public _animationMode ? ) { super ( elementRef ) ; this . radioGroup = radioGroup ; this . _removeUniqueSelectionListener = _radioDispatcher . listen ( ( id , name ) => { if ( id !== this . id && name === this . name ) { this . checked = false ; } } ) ; } /** Focuses the radio button. */ focus ( ) { this . _focusMonitor . focusVia ( this . _inputElement , 'keyboard' ) ; } /**
     * Marks the radio button as needing checking for change detection.
     * This method is exposed because the parent radio group will directly
     * update bound properties of the radio button.
     */ _markForCheck ( ) { this . _changeDetector . markForCheck ( ) ; } ngOnInit ( ) { if ( this . radioGroup ) { this . checked = this . radioGroup . value === this . _value ; this . name = this . radioGroup . name ; } } ngAfterViewInit ( ) { this . _focusMonitor . monitor ( this . _elementRef , true ) . subscribe ( focusOrigin => { if ( ! focusOrigin && this . radioGroup ) { this . radioGroup . _touch ( ) ; } } ) ; } ngOnDestroy ( ) { this . _focusMonitor . stopMonitoring ( this . _elementRef ) ; this . _removeUniqueSelectionListener ( ) ; } /** Dispatch change event with current value. */ private _emitChangeEvent ( ) { this . change . emit ( new MatRadioChange ( this , this . _value ) ) ; } _isRippleDisabled ( ) { return this . disableRipple || this . disabled ; } _onInputClick ( event ) { event . stopPropagation ( ) ; } /**
     * Triggered when the radio button received a click or the input recognized any change.
     * Clicking on a label element, will trigger a change event on the associated input.
     */ _onInputChange ( event ) { event . stopPropagation ( ) ; const groupValueChanged = this . radioGroup && this . value !== this . radioGroup . value ; this . checked = true ; this . _emitChangeEvent ( ) ; if ( this . radioGroup ) { this . radioGroup . _controlValueAccessorChangeFn ( this . value ) ; this . radioGroup . _touch ( ) ; if ( groupValueChanged ) { this . radioGroup . _emitChangeEvent ( ) ; } } } }