From 3ca801a903d86f350e49a53625973f9a05127ddf Mon Sep 17 00:00:00 2001 From: Austin Date: Fri, 10 Nov 2017 13:04:47 -0600 Subject: [PATCH] fix: consistently coerce boolean and number properties (#7283) --- src/cdk/accordion/accordion-item.ts | 7 +++++-- src/cdk/coercion/number-property.ts | 2 ++ src/lib/datepicker/datepicker.ts | 13 ++++++++++--- src/lib/expansion/expansion-panel.ts | 11 ++++++++++- src/lib/menu/menu-directive.ts | 10 +++++++++- src/lib/slide-toggle/slide-toggle.ts | 2 +- src/lib/tabs/tab-group.ts | 6 ++++-- src/lib/tabs/tab-header.ts | 2 ++ 8 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/cdk/accordion/accordion-item.ts b/src/cdk/accordion/accordion-item.ts index 1323bf00cca3..f058b3e5d4bb 100644 --- a/src/cdk/accordion/accordion-item.ts +++ b/src/cdk/accordion/accordion-item.ts @@ -17,6 +17,7 @@ import { } from '@angular/core'; import {UniqueSelectionDispatcher} from '@angular/cdk/collections'; import {CdkAccordion} from './accordion'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; /** Used to generate unique ID for each accordion item. */ let nextId = 0; @@ -41,8 +42,10 @@ export class CdkAccordionItem implements OnDestroy { /** Whether the AccordionItem is expanded. */ @Input() - get expanded(): boolean { return this._expanded; } - set expanded(expanded: boolean) { + get expanded(): any { return this._expanded; } + set expanded(expanded: any) { + expanded = coerceBooleanProperty(expanded); + // Only emit events and update the internal value if the value changes. if (this._expanded !== expanded) { this._expanded = expanded; diff --git a/src/cdk/coercion/number-property.ts b/src/cdk/coercion/number-property.ts index d10568e2700f..38d783974a6b 100644 --- a/src/cdk/coercion/number-property.ts +++ b/src/cdk/coercion/number-property.ts @@ -7,6 +7,8 @@ */ /** Coerces a data-bound value (typically a string) to a number. */ +export function coerceNumberProperty(value: any): number; +export function coerceNumberProperty(value: any, fallback: D): number | D; export function coerceNumberProperty(value: any, fallbackValue = 0) { // parseFloat(value) handles most of the cases we're interested in (it treats null, empty string, // and other non-number values as NaN, where Number just uses 0) but it considers the string diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index acab5074036a..ae0bd02aab63 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -145,15 +145,22 @@ export class MatDatepicker implements OnDestroy { * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather * than a popup and elements have more padding to allow for bigger touch targets. */ - @Input() touchUi = false; + @Input() + get touchUi(): boolean { + return this._touchUi; + } + set touchUi(value: boolean) { + this._touchUi = coerceBooleanProperty(value); + } + private _touchUi = false; /** Whether the datepicker pop-up should be disabled. */ @Input() - get disabled() { + get disabled(): boolean { return this._disabled === undefined && this._datepickerInput ? this._datepickerInput.disabled : !!this._disabled; } - set disabled(value: any) { + set disabled(value: boolean) { const newValue = coerceBooleanProperty(value); if (newValue !== this._disabled) { diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts index 2c5eed73a982..f04cb2cdc44a 100644 --- a/src/lib/expansion/expansion-panel.ts +++ b/src/lib/expansion/expansion-panel.ts @@ -26,6 +26,7 @@ import {UniqueSelectionDispatcher} from '@angular/cdk/collections'; import {CanDisable, mixinDisabled} from '@angular/material/core'; import {Subject} from 'rxjs/Subject'; import {MatAccordion} from './accordion'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; /** Workaround for https://github.com/angular/angular/issues/17849 */ export const _CdkAccordionItem = CdkAccordionItem; @@ -91,8 +92,16 @@ export type MatExpansionPanelState = 'expanded' | 'collapsed'; }) export class MatExpansionPanel extends _MatExpansionPanelMixinBase implements CanDisable, OnChanges, OnDestroy { + /** Whether the toggle indicator should be hidden. */ - @Input() hideToggle: boolean = false; + @Input() + get hideToggle(): boolean { + return this._hideToggle; + } + set hideToggle(value: boolean) { + this._hideToggle = coerceBooleanProperty(value); + } + private _hideToggle = false; /** Stream that emits for changes in `@Input` properties. */ _inputChanges = new Subject(); diff --git a/src/lib/menu/menu-directive.ts b/src/lib/menu/menu-directive.ts index 3c8b777aa08f..c3ef87da5242 100644 --- a/src/lib/menu/menu-directive.ts +++ b/src/lib/menu/menu-directive.ts @@ -39,6 +39,7 @@ import {throwMatMenuInvalidPositionX, throwMatMenuInvalidPositionY} from './menu import {MatMenuItem} from './menu-item'; import {MatMenuPanel} from './menu-panel'; import {MenuPositionX, MenuPositionY} from './menu-positions'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; /** Default `mat-menu` options that can be overridden. */ @@ -122,7 +123,14 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy { @ContentChildren(MatMenuItem) items: QueryList; /** Whether the menu should overlap its trigger. */ - @Input() overlapTrigger = this._defaultOptions.overlapTrigger; + @Input() + set overlapTrigger(value: boolean) { + this._overlapTrigger = coerceBooleanProperty(value); + } + get overlapTrigger(): boolean { + return this._overlapTrigger; + } + private _overlapTrigger: boolean = this._defaultOptions.overlapTrigger; /** * This method takes classes set on the host mat-menu element and applies them on the diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index fe4138a59e2f..7d0110bbde97 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -124,7 +124,7 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro @Input() get checked(): boolean { return this._checked; } set checked(value) { - this._checked = !!value; + this._checked = coerceBooleanProperty(value); this._changeDetectorRef.markForCheck(); } /** An event will be dispatched each time the slide-toggle changes its value. */ diff --git a/src/lib/tabs/tab-group.ts b/src/lib/tabs/tab-group.ts index a41bd2225867..da581c60b5bb 100644 --- a/src/lib/tabs/tab-group.ts +++ b/src/lib/tabs/tab-group.ts @@ -23,7 +23,7 @@ import { ViewChild, ViewEncapsulation, } from '@angular/core'; -import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion'; import {Subscription} from 'rxjs/Subscription'; import {MatTab} from './tab'; import {MatTabHeader} from './tab-header'; @@ -109,7 +109,9 @@ export class MatTabGroup extends _MatTabGroupMixinBase implements AfterContentIn /** The index of the active tab. */ @Input() - set selectedIndex(value: number | null) { this._indexToSelect = value; } + set selectedIndex(value: number | null) { + this._indexToSelect = coerceNumberProperty(value, null); + } get selectedIndex(): number | null { return this._selectedIndex; } private _selectedIndex: number | null = null; diff --git a/src/lib/tabs/tab-header.ts b/src/lib/tabs/tab-header.ts index e7b3f26e391e..31b62051ba24 100644 --- a/src/lib/tabs/tab-header.ts +++ b/src/lib/tabs/tab-header.ts @@ -31,6 +31,7 @@ import {CanDisableRipple, mixinDisableRipple} from '@angular/material/core'; import {merge} from 'rxjs/observable/merge'; import {of as observableOf} from 'rxjs/observable/of'; import {Subscription} from 'rxjs/Subscription'; +import {coerceNumberProperty} from '@angular/cdk/coercion'; import {MatInkBar} from './ink-bar'; import {MatTabLabelWrapper} from './tab-label-wrapper'; import {ViewportRuler} from '@angular/cdk/scrolling'; @@ -120,6 +121,7 @@ export class MatTabHeader extends _MatTabHeaderMixinBase @Input() get selectedIndex(): number { return this._selectedIndex; } set selectedIndex(value: number) { + value = coerceNumberProperty(value); this._selectedIndexChanged = this._selectedIndex != value; this._selectedIndex = value; this._focusIndex = value;