From 9baaaeb0a173f3a9e1ed8399ee8c694bfe5af400 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Mon, 7 Aug 2017 23:07:39 -0700 Subject: [PATCH] address comments --- src/lib/autocomplete/autocomplete-trigger.ts | 24 ++-- src/lib/form-field/error.ts | 26 +++++ src/lib/form-field/form-field-control.ts | 53 +++++++++ src/lib/form-field/form-field.ts | 109 ++----------------- src/lib/form-field/hint.ts | 32 ++++++ src/lib/form-field/index.ts | 20 ++-- src/lib/form-field/placeholder.ts | 16 +++ src/lib/form-field/prefix.ts | 16 +++ src/lib/form-field/suffix.ts | 16 +++ src/lib/input/index.ts | 5 +- src/lib/input/input.spec.ts | 4 +- src/lib/input/input.ts | 2 +- src/lib/public_api.ts | 3 + 13 files changed, 198 insertions(+), 128 deletions(-) create mode 100644 src/lib/form-field/error.ts create mode 100644 src/lib/form-field/form-field-control.ts create mode 100644 src/lib/form-field/hint.ts create mode 100644 src/lib/form-field/placeholder.ts create mode 100644 src/lib/form-field/prefix.ts create mode 100644 src/lib/form-field/suffix.ts diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index b29972a920e5..234927443da4 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -41,7 +41,6 @@ import {MdOptionSelectionChange, MdOption} from '../core/option/option'; import {ENTER, UP_ARROW, DOWN_ARROW, ESCAPE} from '../core/keyboard/keycodes'; import {Directionality} from '../core/bidi/index'; import {MdFormField} from '../form-field/index'; -import {MdInput} from '../input/input'; import {Subscription} from 'rxjs/Subscription'; import {merge} from 'rxjs/observable/merge'; import {fromEvent} from 'rxjs/observable/fromEvent'; @@ -154,7 +153,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { private _changeDetectorRef: ChangeDetectorRef, @Inject(MD_AUTOCOMPLETE_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _dir: Directionality, - @Optional() @Host() private _inputContainer: MdFormField, + @Optional() @Host() private _formField: MdFormField, @Optional() @Inject(DOCUMENT) private _document: any) {} ngOnDestroy() { @@ -247,8 +246,8 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { fromEvent(this._document, 'touchend') )).call(filter, (event: MouseEvent | TouchEvent) => { const clickTarget = event.target as HTMLElement; - const inputContainer = this._inputContainer ? - this._inputContainer._elementRef.nativeElement : null; + const inputContainer = this._formField ? + this._formField._elementRef.nativeElement : null; return this._panelOpen && clickTarget !== this._element.nativeElement && @@ -330,8 +329,8 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { * This method manually floats the placeholder until the panel can be closed. */ private _floatPlaceholder(): void { - if (this._inputContainer && this._inputContainer.floatPlaceholder === 'auto') { - this._inputContainer.floatPlaceholder = 'always'; + if (this._formField && this._formField.floatPlaceholder === 'auto') { + this._formField.floatPlaceholder = 'always'; this._manuallyFloatingPlaceholder = true; } } @@ -339,7 +338,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { /** If the placeholder has been manually elevated, return it to its normal state. */ private _resetPlaceholder(): void { if (this._manuallyFloatingPlaceholder) { - this._inputContainer.floatPlaceholder = 'auto'; + this._formField.floatPlaceholder = 'auto'; this._manuallyFloatingPlaceholder = false; } } @@ -409,11 +408,10 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { // The display value can also be the number zero and shouldn't fall back to an empty string. const inputValue = toDisplay != null ? toDisplay : ''; - // If it's used in a Material container, we should set it through - // the property so it can go through the change detection. - if (this._inputContainer && - this._inputContainer._control instanceof MdInput) { - this._inputContainer._control.value = inputValue; + // If it's used within a `MdFormField`, we should set it through the property so it can go + // through change detection. + if (this._formField) { + this._formField._control.value = inputValue; } else { this._element.nativeElement.value = inputValue; } @@ -471,7 +469,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { } private _getConnectedElement(): ElementRef { - return this._inputContainer ? this._inputContainer._connectionContainerRef : this._element; + return this._formField ? this._formField._connectionContainerRef : this._element; } /** Returns the width of the input element, so the panel width can match it. */ diff --git a/src/lib/form-field/error.ts b/src/lib/form-field/error.ts new file mode 100644 index 000000000000..40d5bb1762ec --- /dev/null +++ b/src/lib/form-field/error.ts @@ -0,0 +1,26 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive, Input} from '@angular/core'; + + +let nextUniqueId = 0; + + +/** Single error message to be shown underneath the form field. */ +@Directive({ + selector: 'md-error, mat-error', + host: { + 'class': 'mat-error', + 'role': 'alert', + '[attr.id]': 'id', + } +}) +export class MdError { + @Input() id: string = `mat-error-${nextUniqueId++}`; +} diff --git a/src/lib/form-field/form-field-control.ts b/src/lib/form-field/form-field-control.ts new file mode 100644 index 000000000000..5af51b1f31ac --- /dev/null +++ b/src/lib/form-field/form-field-control.ts @@ -0,0 +1,53 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Observable} from 'rxjs/Observable'; +import {NgControl} from '@angular/forms'; + + +/** An interface which allows a control to work inside of a `MdFormField`. */ +export abstract class MdFormFieldControl { + /** + * Stream that emits whenever the state of the control changes such that the parent `MdFormField` + * needs to run change detection. + */ + stateChanges: Observable; + + /** The value of the control. */ + value: any; + + /** Gets the element ID for this control. */ + abstract getId(): string; + + /** Gets the placeholder for this control. */ + abstract getPlaceholder(): string; + + /** Gets the NgControl for this control. */ + abstract getNgControl(): NgControl | null; + + /** Whether the control is focused. */ + abstract isFocused(): boolean; + + /** Whether the control is empty. */ + abstract isEmpty(): boolean; + + /** Whether the control is required. */ + abstract isRequired(): boolean; + + /** Whether the control is disabled. */ + abstract isDisabled(): boolean; + + /** Whether the control is in an error state. */ + abstract isErrorState(): boolean; + + /** Sets the list of element IDs that currently describe this control. */ + abstract setDescribedByIds(ids: string[]): void; + + /** Focuses this control. */ + abstract focus(): void; +} diff --git a/src/lib/form-field/form-field.ts b/src/lib/form-field/form-field.ts index 54fc87a2a537..8cf704c675d0 100644 --- a/src/lib/form-field/form-field.ts +++ b/src/lib/form-field/form-field.ts @@ -15,7 +15,6 @@ import { Component, ContentChild, ContentChildren, - Directive, ElementRef, Inject, Input, @@ -26,7 +25,6 @@ import { } from '@angular/core'; import {animate, state, style, transition, trigger} from '@angular/animations'; import {coerceBooleanProperty} from '../core'; -import {NgControl} from '@angular/forms'; import { getMdFormFieldDuplicatedHintError, getMdFormFieldMissingControlError, @@ -38,110 +36,21 @@ import { PlaceholderOptions } from '../core/placeholder/placeholder-options'; import {startWith} from '@angular/cdk/rxjs'; -import {Observable} from 'rxjs/Observable'; - -let nextUniqueId = 0; - - -/** - * The placeholder directive. The content can declare this to implement more - * complex placeholders. - */ -@Directive({ - selector: 'md-placeholder, mat-placeholder' -}) -export class MdPlaceholder {} - - -/** Hint text to be shown underneath the form field control. */ -@Directive({ - selector: 'md-hint, mat-hint', - host: { - 'class': 'mat-hint', - '[class.mat-right]': 'align == "end"', - '[attr.id]': 'id', - } -}) -export class MdHint { - /** Whether to align the hint label at the start or end of the line. */ - @Input() align: 'start' | 'end' = 'start'; - - /** Unique ID for the hint. Used for the aria-describedby on the form field control. */ - @Input() id: string = `md-hint-${nextUniqueId++}`; -} - - -/** Single error message to be shown underneath the form field. */ -@Directive({ - selector: 'md-error, mat-error', - host: { - 'class': 'mat-error', - 'role': 'alert', - '[attr.id]': 'id', - } -}) -export class MdError { - @Input() id: string = `md-error-${nextUniqueId++}`; -} +import {MdError} from './error'; +import {MdFormFieldControl} from './form-field-control'; +import {MdHint} from './hint'; +import {MdPlaceholder} from './placeholder'; +import {MdPrefix} from './prefix'; +import {MdSuffix} from './suffix'; -/** Prefix to be placed the the front of the form field. */ -@Directive({ - selector: '[mdPrefix], [matPrefix]', -}) -export class MdPrefix {} - - -/** Suffix to be placed at the end of the form field. */ -@Directive({ - selector: '[mdSuffix], [matSuffix]', -}) -export class MdSuffix {} - - -/** An interface which allows a control to work inside of a `MdFormField`. */ -export abstract class MdFormFieldControl { - /** - * Stream that emits whenever the state of the control changes such that the parent `MdFormField` - * needs to run change detection. - */ - stateChanges: Observable; - - /** Gets the element ID for this control. */ - abstract getId(): string; - - /** Gets the placeholder for this control. */ - abstract getPlaceholder(): string; - - /** Gets the NgControl for this control. */ - abstract getNgControl(): NgControl | null; - - /** Whether the control is focused. */ - abstract isFocused(): boolean; - - /** Whether the control is empty. */ - abstract isEmpty(): boolean; - - /** Whether the control is required. */ - abstract isRequired(): boolean; - - /** Whether the control is disabled. */ - abstract isDisabled(): boolean; - - /** Whether the control is in an error state. */ - abstract isErrorState(): boolean; - - /** Sets the list of element IDs that currently describe this control. */ - abstract setDescribedByIds(ids: string[]): void; - - /** Focuses this control. */ - abstract focus(): void; -} +let nextUniqueId = 0; /** Container for form controls that applies Material Design styling and behavior. */ @Component({ moduleId: module.id, + // TODO(mmalerba): the input-container selectors and classes are deprecated and will be removed. selector: 'md-input-container, mat-input-container, md-form-field, mat-form-field', templateUrl: 'form-field.html', // MdInput is a directive and can't have styles, so we need to include its styles here. @@ -158,8 +67,6 @@ export abstract class MdFormFieldControl { ]), ], host: { - // Remove align attribute to prevent it from interfering with layout. - '[attr.align]': 'null', 'class': 'mat-input-container mat-form-field', '[class.mat-input-invalid]': '_control.isErrorState()', '[class.mat-form-field-invalid]': '_control.isErrorState()', diff --git a/src/lib/form-field/hint.ts b/src/lib/form-field/hint.ts new file mode 100644 index 000000000000..3a5e21857aeb --- /dev/null +++ b/src/lib/form-field/hint.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive, Input} from '@angular/core'; + + +let nextUniqueId = 0; + + +/** Hint text to be shown underneath the form field control. */ +@Directive({ + selector: 'md-hint, mat-hint', + host: { + 'class': 'mat-hint', + '[class.mat-right]': 'align == "end"', + '[attr.id]': 'id', + // Remove align attribute to prevent it from interfering with layout. + '[attr.align]': 'null', + } +}) +export class MdHint { + /** Whether to align the hint label at the start or end of the line. */ + @Input() align: 'start' | 'end' = 'start'; + + /** Unique ID for the hint. Used for the aria-describedby on the form field control. */ + @Input() id: string = `mat-hint-${nextUniqueId++}`; +} diff --git a/src/lib/form-field/index.ts b/src/lib/form-field/index.ts index 04cbc1533f63..71d8b6164677 100644 --- a/src/lib/form-field/index.ts +++ b/src/lib/form-field/index.ts @@ -7,14 +7,12 @@ */ import {NgModule} from '@angular/core'; -import { - MdError, - MdHint, - MdFormField, - MdPlaceholder, - MdPrefix, - MdSuffix -} from './form-field'; +import {MdError} from './error'; +import {MdFormField} from './form-field'; +import {MdHint} from './hint'; +import {MdPlaceholder} from './placeholder'; +import {MdPrefix} from './prefix'; +import {MdSuffix} from './suffix'; import {CommonModule} from '@angular/common'; import {PlatformModule} from '../core/platform/index'; @@ -44,6 +42,12 @@ import {PlatformModule} from '../core/platform/index'; export class MdFormFieldModule {} +export * from './error'; export * from './form-field'; +export * from './form-field-control'; export * from './form-field-errors'; +export * from './hint'; +export * from './placeholder'; +export * from './prefix'; +export * from './suffix'; diff --git a/src/lib/form-field/placeholder.ts b/src/lib/form-field/placeholder.ts new file mode 100644 index 000000000000..260a4b8fef91 --- /dev/null +++ b/src/lib/form-field/placeholder.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive} from '@angular/core'; + + +/** The floating placeholder for an `MdFormField`. */ +@Directive({ + selector: 'md-placeholder, mat-placeholder' +}) +export class MdPlaceholder {} diff --git a/src/lib/form-field/prefix.ts b/src/lib/form-field/prefix.ts new file mode 100644 index 000000000000..3ac184b14331 --- /dev/null +++ b/src/lib/form-field/prefix.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive} from '@angular/core'; + + +/** Prefix to be placed the the front of the form field. */ +@Directive({ + selector: '[mdPrefix], [matPrefix]', +}) +export class MdPrefix {} diff --git a/src/lib/form-field/suffix.ts b/src/lib/form-field/suffix.ts new file mode 100644 index 000000000000..be70eec03959 --- /dev/null +++ b/src/lib/form-field/suffix.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive} from '@angular/core'; + + +/** Suffix to be placed at the end of the form field. */ +@Directive({ + selector: '[mdSuffix], [matSuffix]', +}) +export class MdSuffix {} diff --git a/src/lib/input/index.ts b/src/lib/input/index.ts index 55caf81f7187..495d4e8c37f7 100644 --- a/src/lib/input/index.ts +++ b/src/lib/input/index.ts @@ -25,9 +25,8 @@ import {MdFormFieldModule} from '../form-field/index'; PlatformModule, ], exports: [ - // TODO(mmalerba): We import and re-export the form field module since all existing users of - // `MdInput` will need this to continue using `md-input-container`. We may want to keep this - // long term since the `MdInput` directive will almost always be used with `md-form-field`. + // We re-export the `MdFormFieldModule` since `MdInput` will almost always be used together with + // `MdFormField`. MdFormFieldModule, MdInput, MdTextareaAutosize, diff --git a/src/lib/input/input.spec.ts b/src/lib/input/input.spec.ts index 7db414504ceb..83ba4e6b7666 100644 --- a/src/lib/input/input.spec.ts +++ b/src/lib/input/input.spec.ts @@ -502,8 +502,8 @@ describe('MdInputContainer without forms', function () { fixture.detectChanges(); - let hintLabel = fixture.debugElement.query(By.css('.mat-hint')).nativeElement; - let endLabel = fixture.debugElement.query(By.css('.mat-hint[align="end"]')).nativeElement; + let hintLabel = fixture.debugElement.query(By.css('.mat-hint:not(.mat-right)')).nativeElement; + let endLabel = fixture.debugElement.query(By.css('.mat-hint.mat-right')).nativeElement; let input = fixture.debugElement.query(By.css('input')).nativeElement; let ariaValue = input.getAttribute('aria-describedby'); diff --git a/src/lib/input/input.ts b/src/lib/input/input.ts index e2732104e619..2692e14f104d 100644 --- a/src/lib/input/input.ts +++ b/src/lib/input/input.ts @@ -29,7 +29,7 @@ import { MD_ERROR_GLOBAL_OPTIONS } from '../core/error/error-options'; import {Subject} from 'rxjs/Subject'; -import {MdFormFieldControl} from '../form-field/form-field'; +import {MdFormFieldControl} from '../form-field/index'; // Invalid input type. Using one of these will throw an MdInputUnsupportedTypeError. const MD_INPUT_INVALID_TYPES = [ diff --git a/src/lib/public_api.ts b/src/lib/public_api.ts index 7c4bdffb425a..76c73f7f60a0 100644 --- a/src/lib/public_api.ts +++ b/src/lib/public_api.ts @@ -46,3 +46,6 @@ export * from './tabs/index'; export * from './tabs/tab-nav-bar/index'; export * from './toolbar/index'; export * from './tooltip/index'; + +// TODO(mmalerba): Temporary alias to avoid breakages, cleanup later. +export {MdFormField as MdInputContainer} from './form-field/index';