fix(input): remove md-input and md-textarea in favor of md-input-container (#2788)

* remove md-input and md-textarea

* fix demo

* clean up filenames
This commit is contained in:
mmalerba
2017-01-26 10:03:09 -08:00
committed by Andrew Seguin
parent 55b9224893
commit 7b30fdc535
25 changed files with 282 additions and 1858 deletions
-4
View File
@@ -10,8 +10,6 @@
| Text 3 |
<md-radio-button value="option_3">Radio 3</md-radio-button>
| Text 4 |
<md-input placeholder="Input"></md-input>
| Text 5 |
<md-input-container>
<input mdInput placeholder="Input">
</md-input-container>
@@ -32,8 +30,6 @@
| Text 3 |
<md-radio-button value="option_3">Radio 3</md-radio-button>
| Text 4 |
<md-input placeholder="Input"></md-input>
| Text 5 |
<md-input-container>
<input mdInput placeholder="Input">
</md-input-container>
+5 -6
View File
@@ -1,5 +1,4 @@
import {Component} from '@angular/core';
import {MdInput} from '@angular/material';
import {Component, ElementRef} from '@angular/core';
export interface Person {
name: string;
@@ -40,10 +39,10 @@ export class ChipsDemo {
alert(message);
}
add(input: MdInput): void {
if (input.value && input.value.trim() != '') {
this.people.push({ name: input.value.trim() });
input.value = '';
add(input: ElementRef): void {
if (input.nativeElement.value && input.nativeElement.value.trim() != '') {
this.people.push({ name: input.nativeElement.value.trim() });
input.nativeElement.value = '';
}
}
-2
View File
@@ -12,7 +12,6 @@ import {JazzDialog, ContentElementDialog, DialogDemo} from './dialog/dialog-demo
import {RippleDemo} from './ripple/ripple-demo';
import {IconDemo} from './icon/icon-demo';
import {GesturesDemo} from './gestures/gestures-demo';
import {InputDemo} from './input/input-demo';
import {CardDemo} from './card/card-demo';
import {ChipsDemo} from './chips/chips-demo';
import {RadioDemo} from './radio/radio-demo';
@@ -63,7 +62,6 @@ import {InputContainerDemo} from './input/input-container-demo';
GridListDemo,
Home,
IconDemo,
InputDemo,
InputContainerDemo,
JazzDialog,
ContentElementDialog,
-2
View File
@@ -10,7 +10,6 @@ import {LiveAnnouncerDemo} from '../live-announcer/live-announcer-demo';
import {ListDemo} from '../list/list-demo';
import {IconDemo} from '../icon/icon-demo';
import {ToolbarDemo} from '../toolbar/toolbar-demo';
import {InputDemo} from '../input/input-demo';
import {CheckboxDemo} from '../checkbox/checkbox-demo';
import {OverlayDemo} from '../overlay/overlay-demo';
import {PortalDemo} from '../portal/portal-demo';
@@ -51,7 +50,6 @@ export const DEMO_APP_ROUTES: Routes = [
{path: 'projection', component: ProjectionDemo},
{path: 'overlay', component: OverlayDemo},
{path: 'checkbox', component: CheckboxDemo},
{path: 'input', component: InputDemo},
{path: 'input-container', component: InputContainerDemo},
{path: 'toolbar', component: ToolbarDemo},
{path: 'icon', component: IconDemo},
+18 -6
View File
@@ -8,20 +8,32 @@
<h2>Dialog dimensions</h2>
<p>
<md-input [(ngModel)]="config.width" placeholder="Width"></md-input>
<md-input [(ngModel)]="config.height" placeholder="Height"></md-input>
<md-input-container>
<input mdInput [(ngModel)]="config.width" placeholder="Width">
</md-input-container>
<md-input-container>
<input mdInput [(ngModel)]="config.height" placeholder="Height">
</md-input-container>
</p>
<h2>Dialog position</h2>
<p>
<md-input [(ngModel)]="config.position.top" (change)="config.position.bottom = ''" placeholder="Top"></md-input>
<md-input [(ngModel)]="config.position.bottom" (change)="config.position.top = ''" placeholder="Bottom"></md-input>
<md-input-container>
<input mdInput [(ngModel)]="config.position.top" (change)="config.position.bottom = ''" placeholder="Top">
</md-input-container>
<md-input-container>
<input mdInput [(ngModel)]="config.position.bottom" (change)="config.position.top = ''" placeholder="Bottom">
</md-input-container>
</p>
<p>
<md-input [(ngModel)]="config.position.left" (change)="config.position.right = ''" placeholder="Left"></md-input>
<md-input [(ngModel)]="config.position.right" (change)="config.position.left = ''" placeholder="Right"></md-input>
<md-input-container>
<input mdInput [(ngModel)]="config.position.left" (change)="config.position.right = ''" placeholder="Left">
</md-input-container>
<md-input-container>
<input mdInput [(ngModel)]="config.position.right" (change)="config.position.left = ''" placeholder="Right">
</md-input-container>
</p>
<h2>Other options</h2>
+7 -7
View File
@@ -92,7 +92,7 @@
<h4>Input</h4>
<p>
<md-input-container class="demo-full-width">
<input md-input
<input mdInput
#characterCountInputHintExample
placeholder="Character count (100 max)"
maxLength="100"
@@ -104,7 +104,7 @@
<h4>Textarea</h4>
<p>
<md-input-container class="demo-full-width">
<textarea md-input
<textarea mdInput
#characterCountTextareaHintExample
placeholder="Character count (100 max)"
maxLength="100">Hello world. How are you?</textarea>
@@ -150,7 +150,7 @@
<p>
<md-input-container>
<input md-input>
<input mdInput>
<md-placeholder>
I <md-icon class="demo-icons">favorite</md-icon> <b>bold</b> placeholder
</md-placeholder>
@@ -174,7 +174,7 @@
<p>
<md-checkbox [(ngModel)]="requiredField"> Check to make required:</md-checkbox>
<md-input-container>
<input md-input
<input mdInput
[required]="requiredField"
[placeholder]="requiredField ? 'Required field' : 'Not required field'">
</md-input-container>
@@ -210,8 +210,8 @@
</p>
<p>
Empty: <md-input-container><input md-input></md-input-container>
<label>Label: <md-input-container><input md-input></md-input-container></label>
Empty: <md-input-container><input mdInput></md-input-container>
<label>Label: <md-input-container><input mdInput></md-input-container></label>
</p>
</md-card-content>
</md-card>
@@ -233,7 +233,7 @@
<td>{{i+1}}</td>
<td>
<md-input-container>
<input md-input
<input mdInput
type="number"
placeholder="value"
aria-label="hello"
+1 -1
View File
@@ -8,7 +8,7 @@ let max = 5;
moduleId: module.id,
selector: 'input-container-demo',
templateUrl: 'input-container-demo.html',
styleUrls: ['input-demo.css'],
styleUrls: ['input-container-demo.css'],
})
export class InputContainerDemo {
dividerColor: boolean;
-184
View File
@@ -1,184 +0,0 @@
<md-card class="demo-card demo-basic">
<md-toolbar color="primary">Basic</md-toolbar>
<md-card-content>
<form>
<md-input class="demo-full-width" placeholder="Company (disabled)" disabled value="Google">
</md-input>
<table style="width: 100%" cellspacing="0"><tr>
<td><md-input placeholder="First name" style="width: 100%"></md-input></td>
<td><md-input placeholder="Long Last Name That Will Be Truncated" style="width: 100%"></md-input></td>
</tr></table>
<p>
<md-textarea class="demo-full-width" placeholder="Address" value="1600 Amphitheatre Pkway"></md-textarea>
<md-textarea class="demo-full-width" placeholder="Address 2"></md-textarea>
</p>
<table style="width: 100%" cellspacing="0"><tr>
<td><md-input class="demo-full-width" placeholder="City" value="Mountain View"></md-input></td>
<td><md-input class="demo-full-width" placeholder="State" maxlength="2" value="CA"></md-input></td>
<td><md-input #postalCode class="demo-full-width" maxlength="5"
placeholder="Postal Code"
value="94043">
<md-hint align="end">{{postalCode.characterCount}} / 5</md-hint>
</md-input></td>
</tr></table>
</form>
</md-card-content>
</md-card>
<md-card class="demo-card demo-basic">
<md-toolbar color="primary">Prefix + Suffix</md-toolbar>
<md-card-content>
<md-input placeholder="amount" align="end">
<span mdPrefix>$&nbsp;</span>
<span mdSuffix>.00</span>
</md-input>
</md-card-content>
</md-card>
<md-card class="demo-card demo-basic">
<md-toolbar color="primary">Divider Colors</md-toolbar>
<md-card-content>
<h4>Input</h4>
<md-input dividerColor="primary" placeholder="Default Color" value="example"></md-input>
<md-input dividerColor="accent" placeholder="Accent Color" value="example"></md-input>
<md-input dividerColor="warn" placeholder="Warn Color" value="example"></md-input>
<h4>Textarea</h4>
<md-textarea dividerColor="primary" placeholder="Default Color" value="example"></md-textarea>
<md-textarea dividerColor="accent" placeholder="Accent Color" value="example"></md-textarea>
<md-textarea dividerColor="warn" placeholder="Warn Color" value="example"></md-textarea>
</md-card-content>
</md-card>
<md-card class="demo-card demo-basic">
<md-toolbar color="primary">Hints</md-toolbar>
<md-card-content>
<h4>Input</h4>
<p>
<md-input placeholder="Character count (100 max)" maxlength="100" class="demo-full-width"
value="Hello world. How are you?"
#characterCountHintExample>
<md-hint align="end">{{characterCountHintExample.characterCount}} / 100</md-hint>
</md-input>
</p>
<h4>Textarea</h4>
<p>
<md-textarea placeholder="Character count (100 max)" maxlength="100" class="demo-full-width"
value="Hello world. How are you?"
#characterCountHintExampleTextarea>
<md-hint align="end">{{characterCountHintExampleTextarea.characterCount}} / 100</md-hint>
</md-textarea>
</p>
</md-card-content>
</md-card>
<md-card class="demo-card">
<md-card-title>
Hello <md-input [(ngModel)]="name" type="text" placeholder="First name"></md-input>,
how are you?
</md-card-title>
<md-card-content>
<p>
<md-input disabled placeholder="Disabled field" value="Value"></md-input>
<md-input required placeholder="Required field"></md-input>
</p>
<p>
<md-input placeholder="100% width placeholder" style="width: 100%"></md-input>
</p>
<p>
<md-input placeholder="Character count (100 max)" maxlength="100" style="width: 100%"
#input>
<md-hint align="end">{{input.characterCount}} / 100</md-hint>
</md-input>
</p>
<p>
<md-input placeholder="Show Hint Label" style="width: 100%"
hintLabel="Hint label"></md-input>
</p>
<p>
<md-input>
<md-placeholder>
I <md-icon class="demo-icons">favorite</md-icon> <b>bold</b> placeholder
</md-placeholder>
<md-hint>
I also <md-icon class="demo-icons">home</md-icon> <i>italic</i> hint labels
</md-hint>
</md-input>
</p>
<p>
<md-input placeholder="Show Hint Label With Character Count" style="width: 100%"
hintLabel="Hint label" #characterCountHintExampleInput>
<md-hint align="end">{{characterCountHintExampleInput.characterCount}}</md-hint>
</md-input>
</p>
<p>
<md-checkbox [(ngModel)]="dividerColor">Check to change the divider color:</md-checkbox>
<md-input [dividerColor]="dividerColor ? 'primary' : 'accent'"
[placeholder]="dividerColor ? 'Primary color' : 'Accent color'"></md-input>
</p>
<p>
<md-checkbox [(ngModel)]="requiredField"> Check to make required:</md-checkbox>
<md-input [required]="requiredField"
[placeholder]="requiredField ? 'Required field' : 'Not required field'"></md-input>
</p>
<p>
<md-checkbox [(ngModel)]="floatingLabel"> Check to make floating label:</md-checkbox>
<md-input [floatingPlaceholder]="floatingLabel"
[placeholder]="floatingLabel ? 'Floating label' : 'Not floating label'"></md-input>
</p>
<p>
<md-input placeholder="Prefixed" value="example">
<div mdPrefix>Example:&nbsp;</div>
</md-input>
<md-input placeholder="Suffixed" value="123" align="end">
<span mdSuffix>.00 €</span>
</md-input>
<br/>
Both:
<md-input #email placeholder="Email Address" value="angular-core" align="end">
<span mdPrefix><md-icon [class.primary]="email.focused" class="demo-icons demo-transform">email</md-icon>&nbsp;</span>
<span mdSuffix class="demo-transform" [class.primary]="email.focused">&nbsp;@gmail.com</span>
</md-input>
</p>
<p>
Empty: <md-input></md-input>
<label>Label: <md-input></md-input></label>
</p>
</md-card-content>
</md-card>
<md-card class="demo-card">
<table width="100%">
<thead>
<td>Table</td>
<td colspan="3">
<button (click)="addABunch(5)">Add 5</button>
<button (click)="addABunch(10)">Add 10</button>
<button (click)="addABunch(100)">Add 100</button>
<button (click)="addABunch(1000)">Add 1000</button>
</td>
</thead>
<tr *ngFor="let item of items; let i = index">
<td>{{i+1}}</td>
<td>
<md-input type="number" placeholder="value" aria-label="hello" [(ngModel)]="items[i].value"></md-input>
</td>
<td>
<input type="number" [(ngModel)]="items[i].value">
</td>
<td>{{item.value}}</td>
</tr>
</table>
</md-card>
<md-card>
<h2>textarea autosize</h2>
<textarea mdTextareaAutosize class="demo-textarea"></textarea>
</md-card>
-31
View File
@@ -1,31 +0,0 @@
import {Component} from '@angular/core';
let max = 5;
@Component({
moduleId: module.id,
selector: 'input-demo',
templateUrl: 'input-demo.html',
styleUrls: ['input-demo.css'],
})
export class InputDemo {
dividerColor: boolean;
requiredField: boolean;
floatingLabel: boolean;
name: string;
items: any[] = [
{ value: 10 },
{ value: 20 },
{ value: 30 },
{ value: 40 },
{ value: 50 },
];
rows = 8;
addABunch(n: number) {
for (let x = 0; x < n; x++) {
this.items.push({ value: ++max });
}
}
}
+2 -2
View File
@@ -7,7 +7,7 @@
<md-checkbox [(ngModel)]="action">
<p *ngIf="!action">Show button on snack bar</p>
<md-input-container *ngIf="action">
<input md-input
<input mdInput
type="text"
class="demo-button-label-input"
placeholder="Snack bar action label"
@@ -19,7 +19,7 @@
<md-checkbox [(ngModel)]="setAutoHide">
<p *ngIf="!setAutoHide">Auto hide after duration</p>
<md-input-container *ngIf="setAutoHide">
<input md-input
<input mdInput
type="number"
class="demo-button-label-input"
placeholder="Auto Hide Duration in ms"
+9 -3
View File
@@ -18,7 +18,9 @@
<div>
Selected tab index:
<md-input type="number" [(ngModel)]="activeTabIndex"></md-input>
<md-input-container>
<input mdInput type="number" [(ngModel)]="activeTabIndex">
</md-input-container>
</div>
<div class="demo-dynamic-tabs">
@@ -33,7 +35,9 @@
</md-checkbox>
<div>
Position:
<md-input type="number" [(ngModel)]="addTabPosition"></md-input>
<md-input-container>
<input mdInput type="number" [(ngModel)]="addTabPosition">
</md-input-container>
</div>
<button md-raised-button color="primary"
(click)="addTab(createWithLongContent)">
@@ -77,7 +81,9 @@
</div>
<br>
<br>
<md-input placeholder="Tab Label" [(ngModel)]="tab.label"></md-input>
<md-input-container>
<input mdInput placeholder="Tab Label" [(ngModel)]="tab.label">
</md-input-container>
<br><br>
<button md-raised-button
[disabled]="dynamicTabs.length == 1"
+2 -2
View File
@@ -47,7 +47,7 @@ tabs-demo md-card {
margin-top: 8px;
}
md-input {
md-input-container {
width: 100px;
}
@@ -55,4 +55,4 @@ tabs-demo md-card {
width: 100%;
}
}
}
}
+6 -2
View File
@@ -31,12 +31,16 @@
<p>
<strong>Show Delay (ms): </strong>
<md-input type="number" [(ngModel)]="showDelay"></md-input>
<md-input-container>
<input mdInput type="number" [(ngModel)]="showDelay">
</md-input-container>
</p>
<p>
<strong>Hide Delay (ms): </strong>
<md-input type="number" [(ngModel)]="hideDelay"></md-input>
<md-input-container>
<input mdInput type="number" [(ngModel)]="hideDelay">
</md-input-container>
</p>
<strong>Mouse over to</strong>
@@ -47,7 +47,6 @@ export const MAT_ELEMENTS_SELECTOR = `
mat-hint,
mat-icon,
mat-ink-bar,
mat-input,
mat-list,
mat-list-item,
mat-menu,
@@ -105,7 +104,6 @@ export const MD_ELEMENTS_SELECTOR = `
md-hint,
md-icon,
md-ink-bar,
md-input,
md-list,
md-list-item,
md-menu,
-3
View File
@@ -1,6 +1,3 @@
**NOTE: The <code>md-input</code> element is deprecated. <code>md-input-container</code> should be
used instead.**
# md-input-container
Inputs are the basic input component of Material 2. The spec can be found
+1 -1
View File
@@ -1,7 +1,7 @@
import {Component, ViewChild} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {MdInputModule} from './input';
import {MdInputModule} from './index';
import {MdTextareaAutosize} from './autosize';
+40 -1
View File
@@ -1,4 +1,43 @@
import {NgModule, ModuleWithProviders} from '@angular/core';
import {MdPlaceholder, MdInputContainer, MdHint, MdInputDirective} from './input-container';
import {MdTextareaAutosize} from './autosize';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {PlatformModule} from '../core/platform/index';
export * from './autosize'
export * from './input';
export * from './input-container';
export * from './input-container-errors';
@NgModule({
declarations: [
MdPlaceholder,
MdInputContainer,
MdHint,
MdTextareaAutosize,
MdInputDirective
],
imports: [
CommonModule,
FormsModule,
PlatformModule,
],
exports: [
MdPlaceholder,
MdInputContainer,
MdHint,
MdTextareaAutosize,
MdInputDirective
],
})
export class MdInputModule {
/** @deprecated */
static forRoot(): ModuleWithProviders {
return {
ngModule: MdInputModule,
providers: [],
};
}
}
+181 -4
View File
@@ -1,4 +1,19 @@
@import '../core/style/variables';
@import '../core/style/form-common';
$md-input-floating-placeholder-scale-factor: 0.75 !default;
// Gradient for showing the dashed line when the input is disabled.
$md-input-underline-disabled-background-image:
linear-gradient(to right, rgba(0, 0, 0, 0.26) 0%, rgba(0, 0, 0, 0.26) 33%, transparent 0%);
// Applies a floating placeholder above the input itself.
@mixin md-input-placeholder-floating {
display: block;
transform: translateY(-1.35em) scale($md-input-floating-placeholder-scale-factor);
width: 100% / $md-input-floating-placeholder-scale-factor;
}
md-input-container {
display: inline-block;
@@ -14,7 +29,75 @@ md-input-container {
}
}
// Global wrapper. We need to apply margin to the element for spacing, but
// cannot apply it to the host element directly.
.md-input-wrapper {
margin: 1em 0;
// Account for the underline which has 4px of margin + 2px of border.
padding-bottom: 6px;
}
// We use a table layout to baseline align the prefix and suffix classes.
// The underline is outside of it so it can cover all of the elements under
// this table.
// Flex does not respect the baseline. What we really want is akin to a table
// as want an inline-block where elements don't wrap.
.md-input-table {
display: inline-table;
flex-flow: column;
vertical-align: bottom;
width: 100%;
& > * {
display: table-cell;
}
}
.md-input-infix {
position: relative;
}
// The Input element proper.
.md-input-element {
// Font needs to be inherited, because by default <input> has a system font.
font: inherit;
// The Material input should match whatever background it is above.
background: transparent;
// If background matches current background then so should the color for proper contrast
color: currentColor;
// By default, <input> has a padding, border, outline and a default width.
border: none;
outline: none;
padding: 0;
width: 100%;
.md-end & {
text-align: right;
[dir='rtl'] & {
text-align: left;
}
}
// Undo the red box-shadow glow added by Firefox on invalid inputs.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-ui-invalid
&:-moz-ui-invalid {
box-shadow: none;
}
// Pseudo-class for Chrome and Safari auto-fill to move the placeholder to
// the floating position. This is necessary because these browsers do not actually
// fire any events when a form auto-fill is occurring.
// Once the autofill is committed, a change event happen and the regular md-input-container
// classes take over to fulfill this behaviour.
// Assumes the autofill is non-empty.
&:-webkit-autofill + .md-input-placeholder.md-float {
@include md-input-placeholder-floating;
}
// Note that we can't use something like visibility: hidden or
// display: none, because IE ends up preventing the user from
// focusing the input altogether.
@@ -30,12 +113,106 @@ md-input-container {
&:-ms-input-placeholder {
color: transparent;
}
}
.md-end & {
text-align: right;
// The placeholder label. This is invisible unless it is. The logic to show it is
// basically `empty || (float && (!empty || focused))`. Float is dependent on the
// `floatingPlaceholder` input.
.md-input-placeholder {
// The placeholder is after the <input>, but needs to be aligned top-left of the
// infix <div>.
position: absolute;
left: 0;
top: 0;
[dir='rtl'] & {
text-align: left;
font-size: 100%;
pointer-events: none; // We shouldn't catch mouse events (let them through).
z-index: 1;
// Put ellipsis text overflow.
width: 100%;
display: none;
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
transform: translateY(0);
transform-origin: bottom left;
transition: transform $swift-ease-out-duration $swift-ease-out-timing-function,
color $swift-ease-out-duration $swift-ease-out-timing-function,
width $swift-ease-out-duration $swift-ease-out-timing-function;
&.md-empty {
display: block;
cursor: text;
}
// Show the placeholder above the input when it's not empty, or focused.
&.md-float:not(.md-empty), &.md-float.md-focused {
@include md-input-placeholder-floating;
}
[dir='rtl'] & {
transform-origin: bottom right;
left: auto;
right: 0;
}
}
// The underline is what's shown under the input, its prefix and its suffix.
// The ripple is the blue animation coming on top of it.
.md-input-underline {
position: absolute;
height: 1px;
width: 100%;
margin-top: 4px;
border-top-width: 1px;
border-top-style: solid;
&.md-disabled {
@include md-control-disabled-underline();
border-top: 0;
background-position: 0;
}
.md-input-ripple {
position: absolute;
height: 2px;
z-index: 1;
top: -1px;
width: 100%;
transform-origin: top;
opacity: 0;
transform: scaleY(0);
transition: transform $swift-ease-out-duration $swift-ease-out-timing-function,
opacity $swift-ease-out-duration $swift-ease-out-timing-function;
&.md-focused {
opacity: 1;
transform: scaleY(1);
}
}
}
// The hint is shown below the underline. There can be more than one; one at the start
// and one at the end.
.md-hint {
display: block;
position: absolute;
font-size: 75%;
bottom: 0;
&.md-right {
right: 0;
}
[dir='rtl'] & {
right: 0;
left: auto;
&.md-right {
right: auto;
left: 0;
}
}
}
+8 -8
View File
@@ -2,7 +2,7 @@ import {async, TestBed, inject} from '@angular/core/testing';
import {Component} from '@angular/core';
import {FormsModule, ReactiveFormsModule, FormControl} from '@angular/forms';
import {By} from '@angular/platform-browser';
import {MdInputModule} from './input';
import {MdInputModule} from './index';
import {MdInputContainer, MdInputDirective} from './input-container';
import {Platform} from '../core/platform/platform';
import {PlatformModule} from '../core/platform/index';
@@ -443,7 +443,7 @@ class MdInputContainerPlaceholderRequiredTestComponent {}
@Component({
template: `
<md-input-container>
<input md-input>
<input mdInput>
<md-placeholder>{{placeholder}}</md-placeholder>
</md-input-container>`
})
@@ -452,7 +452,7 @@ class MdInputContainerPlaceholderElementTestComponent {
}
@Component({
template: `<md-input-container><input md-input [formControl]="formControl"></md-input-container>`
template: `<md-input-container><input mdInput [formControl]="formControl"></md-input-container>`
})
class MdInputContainerWithFormControl {
formControl = new FormControl();
@@ -466,14 +466,14 @@ class MdInputContainerPlaceholderAttrTestComponent {
}
@Component({
template: `<md-input-container><input md-input><md-hint>{{label}}</md-hint></md-input-container>`
template: `<md-input-container><input mdInput><md-hint>{{label}}</md-hint></md-input-container>`
})
class MdInputContainerHintLabel2TestController {
label: string = '';
}
@Component({
template: `<md-input-container [hintLabel]="label"><input md-input></md-input-container>`
template: `<md-input-container [hintLabel]="label"><input mdInput></md-input-container>`
})
class MdInputContainerHintLabelTestController {
label: string = '';
@@ -496,7 +496,7 @@ class MdInputContainerInvalidPlaceholderTestController {}
@Component({
template: `
<md-input-container hintLabel="Hello">
<input md-input>
<input mdInput>
<md-hint>World</md-hint>
</md-input-container>`
})
@@ -505,7 +505,7 @@ class MdInputContainerInvalidHint2TestController {}
@Component({
template: `
<md-input-container>
<input md-input>
<input mdInput>
<md-hint>Hello</md-hint>
<md-hint>World</md-hint>
</md-input-container>`
@@ -574,7 +574,7 @@ class MdInputContainerWithValueBinding {
@Component({
template: `
<md-input-container [floatingPlaceholder]="false">
<input md-input placeholder="Label">
<input mdInput placeholder="Label">
</md-input-container>
`
})
+2 -6
View File
@@ -68,11 +68,7 @@ export class MdHint {
/** The input directive, used to mark the input that `MdInputContainer` is wrapping. */
@Directive({
// TODO: remove the md-input selector after next version
selector: `
input[mdInput], textarea[mdInput], input[matInput], textarea[matInput],
input[md-input], textarea[md-input], input[mat-input], textarea[mat-input]
`,
selector: `input[mdInput], textarea[mdInput], input[matInput], textarea[matInput]`,
host: {
'class': 'md-input-element',
// Native input properties that are overwritten by Angular inputs need to be synced with
@@ -226,7 +222,7 @@ export class MdInputDirective {
moduleId: module.id,
selector: 'md-input-container, mat-input-container',
templateUrl: 'input-container.html',
styleUrls: ['input.css', 'input-container.css'],
styleUrls: ['input-container.css'],
host: {
// Remove align attribute to prevent it from interfering with layout.
'[attr.align]': 'null',
-96
View File
@@ -1,96 +0,0 @@
<div class="md-input-wrapper">
<div class="md-input-table">
<div class="md-input-prefix"><ng-content select="[mdPrefix], [md-prefix]"></ng-content></div>
<div class="md-input-infix">
<input #input
*ngIf="_elementType === 'input'"
class="md-input-element"
[class.md-end]="align == 'end'"
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledBy"
[attr.aria-disabled]="ariaDisabled"
[attr.aria-required]="ariaRequired"
[attr.aria-invalid]="ariaInvalid"
[attr.autocomplete]="autocomplete"
[attr.autocorrect]="autocorrect"
[attr.autocapitalize]="autocapitalize"
[autofocus]="autofocus"
[disabled]="disabled"
[id]="inputId"
[attr.list]="list"
[attr.max]="max"
[attr.maxlength]="maxlength"
[attr.min]="min"
[attr.minlength]="minlength"
[readonly]="readonly"
[required]="required"
[spellcheck]="spellcheck"
[attr.step]="step"
[attr.tabindex]="tabindex"
[type]="type"
[attr.name]="name"
(focus)="_handleFocus($event)"
(blur)="_handleBlur($event)"
[(ngModel)]="value"
(change)="_handleChange($event)">
<textarea
#input
*ngIf="_elementType === 'textarea'"
class="md-input-element md-input-element-textarea"
[class.md-end]="align == 'end'"
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledBy"
[attr.aria-disabled]="ariaDisabled"
[attr.aria-required]="ariaRequired"
[attr.aria-invalid]="ariaInvalid"
[attr.autocomplete]="autocomplete"
[attr.autocapitalize]="autocapitalize"
[attr.cols]="cols"
[attr.rows]="rows"
[attr.wrap]="wrap"
[autofocus]="autofocus"
[disabled]="disabled"
[id]="inputId"
[attr.maxlength]="maxlength"
[attr.minlength]="minlength"
[readonly]="readonly"
[required]="required"
[spellcheck]="spellcheck"
[attr.tabindex]="tabindex"
[attr.name]="name"
(focus)="_handleFocus($event)"
(blur)="_handleBlur($event)"
[(ngModel)]="value"
(change)="_handleChange($event)"></textarea>
<label class="md-input-placeholder"
[attr.for]="inputId"
[class.md-empty]="empty"
[class.md-focused]="focused"
[class.md-float]="floatingPlaceholder"
[class.md-accent]="dividerColor == 'accent'"
[class.md-warn]="dividerColor == 'warn'"
*ngIf="_hasPlaceholder()">
<ng-content select="md-placeholder"></ng-content>
{{placeholder}}
<span class="md-placeholder-required" *ngIf="required">*</span>
</label>
</div>
<div class="md-input-suffix"><ng-content select="[mdSuffix], [md-suffix]"></ng-content></div>
</div>
<div class="md-input-underline"
[class.md-disabled]="disabled">
<span class="md-input-ripple"
[class.md-focused]="focused"
[class.md-accent]="dividerColor == 'accent'"
[class.md-warn]="dividerColor == 'warn'"></span>
</div>
<div *ngIf="hintLabel != ''" class="md-hint">{{hintLabel}}</div>
<ng-content select="md-hint"></ng-content>
</div>
-202
View File
@@ -1,202 +0,0 @@
@import '../core/style/variables';
@import '../core/style/form-common';
$md-input-floating-placeholder-scale-factor: 0.75 !default;
// Gradient for showing the dashed line when the input is disabled.
$md-input-underline-disabled-background-image:
linear-gradient(to right, rgba(0, 0, 0, 0.26) 0%, rgba(0, 0, 0, 0.26) 33%, transparent 0%);
// Applies a floating placeholder above the input itself.
@mixin md-input-placeholder-floating {
display: block;
transform: translateY(-1.35em) scale($md-input-floating-placeholder-scale-factor);
width: 100% / $md-input-floating-placeholder-scale-factor;
}
md-input, md-textarea {
display: inline-block;
position: relative;
font-family: $md-font-family;
line-height: normal;
// To avoid problems with text-align.
text-align: left;
[dir='rtl'] & {
text-align: right;
}
}
// Global wrapper. We need to apply margin to the element for spacing, but
// cannot apply it to the host element directly.
.md-input-wrapper {
margin: 1em 0;
// Account for the underline which has 4px of margin + 2px of border.
padding-bottom: 6px;
}
// We use a table layout to baseline align the prefix and suffix classes.
// The underline is outside of it so it can cover all of the elements under
// this table.
// Flex does not respect the baseline. What we really want is akin to a table
// as want an inline-block where elements don't wrap.
.md-input-table {
display: inline-table;
flex-flow: column;
vertical-align: bottom;
width: 100%;
& > * {
display: table-cell;
}
}
.md-input-infix {
position: relative;
}
// The Input element proper.
.md-input-element {
// Font needs to be inherited, because by default <input> has a system font.
font: inherit;
// The Material input should match whatever background it is above.
background: transparent;
// If background matches current background then so should the color for proper contrast
color: currentColor;
// By default, <input> has a padding, border, outline and a default width.
border: none;
outline: none;
padding: 0;
width: 100%;
&.md-end {
text-align: right;
[dir='rtl'] & {
text-align: left;
}
}
// Undo the red box-shadow glow added by Firefox on invalid inputs.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-ui-invalid
&:-moz-ui-invalid {
box-shadow: none;
}
// Pseudo-class for Chrome and Safari auto-fill to move the placeholder to
// the floating position. This is necessary because these browsers do not actually
// fire any events when a form auto-fill is occurring.
// Once the autofill is committed, a change event happen and the regular md-input
// classes take over to fulfill this behaviour.
// Assumes the autofill is non-empty.
&:-webkit-autofill + .md-input-placeholder.md-float {
@include md-input-placeholder-floating;
}
}
// The placeholder label. This is invisible unless it is. The logic to show it is
// basically `empty || (float && (!empty || focused))`. Float is dependent on the
// `floatingPlaceholder` input.
.md-input-placeholder {
// The placeholder is after the <input>, but needs to be aligned top-left of the
// infix <div>.
position: absolute;
left: 0;
top: 0;
font-size: 100%;
pointer-events: none; // We shouldn't catch mouse events (let them through).
z-index: 1;
// Put ellipsis text overflow.
width: 100%;
display: none;
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
transform: translateY(0);
transform-origin: bottom left;
transition: transform $swift-ease-out-duration $swift-ease-out-timing-function,
color $swift-ease-out-duration $swift-ease-out-timing-function,
width $swift-ease-out-duration $swift-ease-out-timing-function;
&.md-empty {
display: block;
cursor: text;
}
// Show the placeholder above the input when it's not empty, or focused.
&.md-float:not(.md-empty), &.md-float.md-focused {
@include md-input-placeholder-floating;
}
[dir='rtl'] & {
transform-origin: bottom right;
left: auto;
right: 0;
}
}
// The underline is what's shown under the input, its prefix and its suffix.
// The ripple is the blue animation coming on top of it.
.md-input-underline {
position: absolute;
height: 1px;
width: 100%;
margin-top: 4px;
border-top-width: 1px;
border-top-style: solid;
&.md-disabled {
@include md-control-disabled-underline();
border-top: 0;
background-position: 0;
}
.md-input-ripple {
position: absolute;
height: 2px;
z-index: 1;
top: -1px;
width: 100%;
transform-origin: top;
opacity: 0;
transform: scaleY(0);
transition: transform $swift-ease-out-duration $swift-ease-out-timing-function,
opacity $swift-ease-out-duration $swift-ease-out-timing-function;
&.md-focused {
opacity: 1;
transform: scaleY(1);
}
}
}
// The hint is shown below the underline. There can be more than one; one at the start
// and one at the end.
.md-hint {
display: block;
position: absolute;
font-size: 75%;
bottom: 0;
&.md-right {
right: 0;
}
[dir='rtl'] & {
right: 0;
left: auto;
&.md-right {
right: auto;
left: 0;
}
}
}
-843
View File
@@ -1,843 +0,0 @@
import {
async,
TestBed,
} from '@angular/core/testing';
import {Component} from '@angular/core';
import {FormsModule, ReactiveFormsModule, FormControl} from '@angular/forms';
import {By} from '@angular/platform-browser';
import {MdInput, MdInputModule} from './input';
function isInternetExplorer11() {
return 'ActiveXObject' in window;
}
describe('MdInput', function () {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdInputModule.forRoot(), FormsModule, ReactiveFormsModule],
declarations: [
MdInputNumberTypeConservedTestComponent,
MdInputPlaceholderRequiredTestComponent,
MdInputPlaceholderElementTestComponent,
MdInputPlaceholderAttrTestComponent,
MdInputHintLabel2TestController,
MdInputHintLabelTestController,
MdInputInvalidTypeTestController,
MdInputInvalidPlaceholderTestController,
MdInputInvalidHint2TestController,
MdInputInvalidHintTestController,
MdInputBaseTestController,
MdInputAriaTestController,
MdInputWithBlurAndFocusEvents,
MdInputWithNameTestController,
MdInputWithId,
MdInputWithAutocomplete,
MdInputWithUnboundAutocomplete,
MdInputWithUnboundAutocompleteWithValue,
MdInputWithAutocorrect,
MdInputWithUnboundAutocorrect,
MdInputWithAutocapitalize,
MdInputWithUnboundAutocapitalize,
MdInputWithAutofocus,
MdInputWithUnboundAutofocus,
MdInputWithReadonly,
MdInputWithUnboundReadonly,
MdInputWithSpellcheck,
MdInputWithUnboundSpellcheck,
MdInputWithDisabled,
MdInputWithUnboundDisabled,
MdInputWithRequired,
MdInputWithUnboundRequired,
MdInputWithList,
MdInputWithMax,
MdInputWithMin,
MdInputWithStep,
MdInputWithTabindex,
MdInputDateTestController,
MdInputTextTestController,
MdInputPasswordTestController,
MdInputNumberTestController,
MdTextareaWithBindings,
MdInputWithFormControl,
],
});
TestBed.compileComponents();
}));
it('creates a native <input> element', () => {
let fixture = TestBed.createComponent(MdInputBaseTestController);
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('input'))).toBeTruthy();
});
it('should default to flating placeholders', () => {
let fixture = TestBed.createComponent(MdInputBaseTestController);
fixture.detectChanges();
let mdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance as MdInput;
expect(mdInput.floatingPlaceholder)
.toBe(true, 'Expected MdInput to default to having floating placeholders turned on');
});
it('should not be treated as empty if type is date', () => {
if (isInternetExplorer11()) {
return;
}
let fixture = TestBed.createComponent(MdInputDateTestController);
fixture.componentInstance.placeholder = 'Placeholder';
fixture.detectChanges();
let el = fixture.debugElement.query(By.css('label')).nativeElement;
expect(el).not.toBeNull();
expect(el.className.includes('md-empty')).toBe(false);
});
it('should treat text input type as empty at init', () => {
if (isInternetExplorer11()) {
return;
}
let fixture = TestBed.createComponent(MdInputTextTestController);
fixture.componentInstance.placeholder = 'Placeholder';
fixture.detectChanges();
let el = fixture.debugElement.query(By.css('label')).nativeElement;
expect(el).not.toBeNull();
expect(el.className.includes('md-empty')).toBe(true);
});
it('should treat password input type as empty at init', () => {
if (isInternetExplorer11()) {
return;
}
let fixture = TestBed.createComponent(MdInputPasswordTestController);
fixture.componentInstance.placeholder = 'Placeholder';
fixture.detectChanges();
let el = fixture.debugElement.query(By.css('label')).nativeElement;
expect(el).not.toBeNull();
expect(el.className.includes('md-empty')).toBe(true);
});
it('should treat number input type as empty at init', () => {
if (isInternetExplorer11()) {
return;
}
let fixture = TestBed.createComponent(MdInputNumberTestController);
fixture.componentInstance.placeholder = 'Placeholder';
fixture.detectChanges();
let el = fixture.debugElement.query(By.css('label')).nativeElement;
expect(el).not.toBeNull();
expect(el.className.includes('md-empty')).toBe(true);
});
// TODO(kara): update when core/testing adds fix
it('support ngModel', async(() => {
let fixture = TestBed.createComponent(MdInputBaseTestController);
fixture.detectChanges();
let instance = fixture.componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
instance.model = 'hello';
fixture.detectChanges();
fixture.whenStable().then(() => {
// Temporary workaround, see https://github.com/angular/angular/issues/10148
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(el.value).toBe('hello');
});
});
}));
it('should have a different ID for outer element and internal input', () => {
let fixture = TestBed.createComponent(MdInputWithId);
fixture.detectChanges();
const componentElement: HTMLElement =
fixture.debugElement.query(By.directive(MdInput)).nativeElement;
const inputElement: HTMLInputElement =
fixture.debugElement.query(By.css('input')).nativeElement;
expect(componentElement.id).toBe('test-id');
expect(inputElement.id).toBeTruthy();
expect(inputElement.id).not.toBe(componentElement.id);
});
it('counts characters', async(() => {
let fixture = TestBed.createComponent(MdInputBaseTestController);
let instance = fixture.componentInstance;
fixture.detectChanges();
let inputInstance = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
expect(inputInstance.characterCount).toEqual(0);
instance.model = 'hello';
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(inputInstance.characterCount).toEqual(5);
});
}));
it('copies aria attributes to the inner input', () => {
let fixture = TestBed.createComponent(MdInputAriaTestController);
let instance = fixture.componentInstance;
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el.getAttribute('aria-label')).toEqual('label');
instance.ariaLabel = 'label 2';
fixture.detectChanges();
expect(el.getAttribute('aria-label')).toEqual('label 2');
expect(el.getAttribute('aria-disabled')).toBeTruthy();
});
it(`validates there's only one hint label per side`, () => {
let fixture = TestBed.createComponent(MdInputInvalidHintTestController);
expect(() => fixture.detectChanges()).toThrow();
// TODO(jelbourn): .toThrow(new MdInputDuplicatedHintError('start'));
// See https://github.com/angular/angular/issues/8348
});
it(`validates there's only one hint label per side (attribute)`, () => {
let fixture = TestBed.createComponent(MdInputInvalidHint2TestController);
expect(() => fixture.detectChanges()).toThrow();
// TODO(jelbourn): .toThrow(new MdInputDuplicatedHintError('start'));
// See https://github.com/angular/angular/issues/8348
});
it('validates there\'s only one placeholder', () => {
let fixture = TestBed.createComponent(MdInputInvalidPlaceholderTestController);
expect(() => fixture.detectChanges()).toThrow();
// TODO(jelbourn): .toThrow(new MdInputPlaceholderConflictError());
// See https://github.com/angular/angular/issues/8348
});
it('validates the type', () => {
let fixture = TestBed.createComponent(MdInputInvalidTypeTestController);
// Technically this throws during the OnChanges detection phase,
// so the error is really a ChangeDetectionError and it becomes
// hard to build a full exception to compare with.
// We just check for any exception in this case.
expect(() => fixture.detectChanges()).toThrow(/* new MdInputUnsupportedTypeError('file') */);
});
it('supports hint labels attribute', () => {
let fixture = TestBed.createComponent(MdInputHintLabelTestController);
fixture.detectChanges();
// If the hint label is empty, expect no label.
expect(fixture.debugElement.query(By.css('.md-hint'))).toBeNull();
fixture.componentInstance.label = 'label';
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.md-hint'))).not.toBeNull();
});
it('supports hint labels elements', () => {
let fixture = TestBed.createComponent(MdInputHintLabel2TestController);
fixture.detectChanges();
// In this case, we should have an empty <md-hint>.
let el = fixture.debugElement.query(By.css('md-hint')).nativeElement;
expect(el.textContent).toBeFalsy();
fixture.componentInstance.label = 'label';
fixture.detectChanges();
el = fixture.debugElement.query(By.css('md-hint')).nativeElement;
expect(el.textContent).toBe('label');
});
it('supports placeholder attribute', () => {
let fixture = TestBed.createComponent(MdInputPlaceholderAttrTestComponent);
fixture.detectChanges();
let el = fixture.debugElement.query(By.css('label'));
expect(el).toBeNull();
fixture.componentInstance.placeholder = 'Other placeholder';
fixture.detectChanges();
el = fixture.debugElement.query(By.css('label'));
expect(el).not.toBeNull();
expect(el.nativeElement.textContent).toMatch('Other placeholder');
expect(el.nativeElement.textContent).not.toMatch(/\*/g);
});
it('supports placeholder element', () => {
let fixture = TestBed.createComponent(MdInputPlaceholderElementTestComponent);
fixture.detectChanges();
let el = fixture.debugElement.query(By.css('label'));
expect(el).not.toBeNull();
expect(el.nativeElement.textContent).toMatch('Default Placeholder');
fixture.componentInstance.placeholder = 'Other placeholder';
fixture.detectChanges();
el = fixture.debugElement.query(By.css('label'));
expect(el).not.toBeNull();
expect(el.nativeElement.textContent).toMatch('Other placeholder');
expect(el.nativeElement.textContent).not.toMatch(/\*/g);
});
it('supports placeholder required star', () => {
let fixture = TestBed.createComponent(MdInputPlaceholderRequiredTestComponent);
fixture.detectChanges();
let el = fixture.debugElement.query(By.css('label'));
expect(el).not.toBeNull();
expect(el.nativeElement.textContent).toMatch(/hello\s+\*/g);
});
it('supports number types and conserved its value type from Angular', () => {
let fixture = TestBed.createComponent(MdInputNumberTypeConservedTestComponent);
fixture.detectChanges();
const input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
const inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
// Fake a `change` event being triggered.
inputElement.value = '3';
input._handleChange(<any> {target: inputElement});
fixture.detectChanges();
expect(fixture.componentInstance.value).toBe(3);
expect(typeof fixture.componentInstance.value).toBe('number');
});
it('supports blur and focus events', () => {
let fixture = TestBed.createComponent(MdInputWithBlurAndFocusEvents);
const testComponent = fixture.componentInstance;
const inputComponent = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
const fakeEvent = <FocusEvent>{};
spyOn(testComponent, 'onFocus');
spyOn(testComponent, 'onBlur');
expect(testComponent.onFocus).not.toHaveBeenCalled();
expect(testComponent.onBlur).not.toHaveBeenCalled();
inputComponent._handleFocus(fakeEvent);
expect(testComponent.onFocus).toHaveBeenCalledWith(fakeEvent);
inputComponent._handleBlur(fakeEvent);
expect(testComponent.onBlur).toHaveBeenCalledWith(fakeEvent);
});
it('supports the autoComplete attribute', () => {
let fixture = TestBed.createComponent(MdInputWithAutocomplete);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('autocomplete')).toBeNull();
input.autocomplete = 'on';
fixture.detectChanges();
expect(el.getAttribute('autocomplete')).toEqual('on');
});
it('supports the autoCorrect attribute', () => {
let fixture = TestBed.createComponent(MdInputWithAutocorrect);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('autocorrect')).toBeNull();
input.autocorrect = 'on';
fixture.detectChanges();
expect(el.getAttribute('autocorrect')).toEqual('on');
});
it('supports the autoCapitalize attribute', () => {
let fixture = TestBed.createComponent(MdInputWithAutocapitalize);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('autocapitalize')).toBeNull();
input.autocapitalize = 'on';
fixture.detectChanges();
expect(el.getAttribute('autocapitalize')).toEqual('on');
});
it('supports the autoComplete attribute as an unbound attribute', () => {
let fixture = TestBed.createComponent(MdInputWithUnboundAutocomplete);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('autocomplete')).toEqual('');
});
it('supports the autoComplete attribute as an unbound value attribute', () => {
let fixture = TestBed.createComponent(MdInputWithUnboundAutocompleteWithValue);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('autocomplete')).toEqual('name');
});
it('supports the autoFocus attribute', () => {
let fixture = TestBed.createComponent(MdInputWithAutofocus);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('autofocus')).toBeNull();
input.autofocus = true;
fixture.detectChanges();
expect(el.getAttribute('autofocus')).toEqual('');
});
it('supports the autoFocus attribute as an unbound attribute', () => {
let fixture = TestBed.createComponent(MdInputWithUnboundAutofocus);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('autofocus')).toEqual('');
});
it('supports the disabled attribute', () => {
let fixture = TestBed.createComponent(MdInputWithDisabled);
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
input.disabled = false;
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
fixture.detectChanges();
expect(el.getAttribute('disabled')).toEqual(null);
fixture.componentInstance.disabled = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(el.getAttribute('disabled')).toEqual('');
});
});
it('supports the disabled attribute as an unbound attribute', () => {
let fixture = TestBed.createComponent(MdInputWithUnboundDisabled);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
fixture.whenStable().then(() => {
expect(el.getAttribute('disabled')).toEqual('');
});
});
it('supports the list attribute', () => {
let fixture = TestBed.createComponent(MdInputWithList);
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
input.disabled = false;
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
fixture.detectChanges();
expect(el.getAttribute('list')).toEqual(null);
input.list = 'datalist-id';
fixture.detectChanges();
expect(el.getAttribute('list')).toEqual('datalist-id');
});
it('supports the max attribute', () => {
let fixture = TestBed.createComponent(MdInputWithMax);
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
input.disabled = false;
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
fixture.detectChanges();
expect(el.getAttribute('max')).toEqual(null);
input.max = 10;
fixture.detectChanges();
expect(el.getAttribute('max')).toEqual('10');
input.max = '2000-01-02';
fixture.detectChanges();
expect(el.getAttribute('max')).toEqual('2000-01-02');
});
it('supports the min attribute', () => {
let fixture = TestBed.createComponent(MdInputWithMin);
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
input.disabled = false;
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
fixture.detectChanges();
expect(el.getAttribute('min')).toEqual(null);
input.min = 10;
fixture.detectChanges();
expect(el.getAttribute('min')).toEqual('10');
input.min = '2000-01-02';
fixture.detectChanges();
expect(el.getAttribute('min')).toEqual('2000-01-02');
});
it('supports the readOnly attribute', () => {
let fixture = TestBed.createComponent(MdInputWithReadonly);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
expect(el).not.toBeNull();
expect(el.getAttribute('readonly')).toBeNull();
input.readonly = true;
fixture.detectChanges();
expect(el.getAttribute('readonly')).toEqual('');
});
it('supports the readOnly attribute as an unbound attribute', () => {
let fixture = TestBed.createComponent(MdInputWithUnboundReadonly);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('readonly')).toEqual('');
});
it('supports the required attribute', () => {
let fixture = TestBed.createComponent(MdInputWithRequired);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('required')).toBeNull();
input.required = true;
fixture.detectChanges();
expect(el.getAttribute('required')).toEqual('');
});
it('supports the required attribute as an unbound attribute', () => {
let fixture = TestBed.createComponent(MdInputWithUnboundRequired);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('required')).toEqual('');
});
it('supports the spellCheck attribute', () => {
let fixture = TestBed.createComponent(MdInputWithSpellcheck);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('spellcheck')).toEqual('false');
input.spellcheck = true;
fixture.detectChanges();
expect(el.getAttribute('spellcheck')).toEqual('true');
});
it('supports the spellCheck attribute as an unbound attribute', () => {
let fixture = TestBed.createComponent(MdInputWithUnboundSpellcheck);
fixture.detectChanges();
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('spellcheck')).toEqual('true');
});
it('supports the step attribute', () => {
let fixture = TestBed.createComponent(MdInputWithStep);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('step')).toEqual(null);
input.step = 0.5;
fixture.detectChanges();
expect(el.getAttribute('step')).toEqual('0.5');
});
it('supports the tabIndex attribute', () => {
let fixture = TestBed.createComponent(MdInputWithTabindex);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
expect(el).not.toBeNull();
expect(el.getAttribute('tabindex')).toEqual(null);
input.tabindex = 1;
fixture.detectChanges();
expect(el.getAttribute('tabindex')).toEqual('1');
});
it('supports a name attribute', () => {
let fixture = TestBed.createComponent(MdInputWithNameTestController);
fixture.detectChanges();
const inputElement: HTMLInputElement = fixture.debugElement.query(By.css('input'))
.nativeElement;
expect(inputElement.name).toBe('some-name');
});
it('toggles the disabled state when used with a FormControl', () => {
let fixture = TestBed.createComponent(MdInputWithFormControl);
fixture.detectChanges();
let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance;
let testComponent: MdInputWithFormControl = fixture.debugElement.componentInstance;
expect(input.disabled).toBe(false);
testComponent.formControl.disable();
fixture.detectChanges();
expect(input.disabled).toBe(true);
testComponent.formControl.enable();
fixture.detectChanges();
expect(input.disabled).toBe(false);
});
describe('md-textarea', () => {
it('supports the rows, cols, and wrap attributes', () => {
let fixture = TestBed.createComponent(MdTextareaWithBindings);
fixture.detectChanges();
const textarea: HTMLTextAreaElement = fixture.nativeElement.querySelector('textarea');
expect(textarea.rows).toBe(4);
expect(textarea.cols).toBe(8);
expect(textarea.wrap).toBe('hard');
});
});
});
@Component({template: `<md-input id="test-id"></md-input>`})
class MdInputWithId {
value: number = 0;
}
@Component({template: `<md-input type="number" [(ngModel)]="value"></md-input>`})
class MdInputNumberTypeConservedTestComponent {
value: number = 0;
}
@Component({template: `<md-input required placeholder="hello"></md-input>`})
class MdInputPlaceholderRequiredTestComponent {
}
@Component({template: `<md-input> <md-placeholder>{{placeholder}}</md-placeholder> </md-input>`})
class MdInputPlaceholderElementTestComponent {
placeholder: string = 'Default Placeholder';
}
@Component({template: `<md-input [placeholder]="placeholder"></md-input>`})
class MdInputPlaceholderAttrTestComponent {
placeholder: string = '';
}
@Component({template: `<md-input> <md-hint>{{label}}</md-hint> </md-input>`})
class MdInputHintLabel2TestController {
label: string = '';
}
@Component({template: `<md-input [hintLabel]="label"></md-input>`})
class MdInputHintLabelTestController {
label: string = '';
}
@Component({template: `<md-input type="file"></md-input>`})
class MdInputInvalidTypeTestController { }
@Component({
template: `
<md-input placeholder="Hello">
<md-placeholder>World</md-placeholder>
</md-input>`
})
class MdInputInvalidPlaceholderTestController { }
@Component({
template: `
<md-input hintLabel="Hello">
<md-hint>World</md-hint>
</md-input>`
})
class MdInputInvalidHint2TestController { }
@Component({
template: `
<md-input>
<md-hint>Hello</md-hint>
<md-hint>World</md-hint>
</md-input>`
})
class MdInputInvalidHintTestController { }
@Component({template: `<md-input [(ngModel)]="model"></md-input>`})
class MdInputBaseTestController {
model: any = '';
}
@Component({template:
`<md-input [aria-label]="ariaLabel" [aria-disabled]="ariaDisabled"></md-input>`})
class MdInputAriaTestController {
ariaLabel: string = 'label';
ariaDisabled: boolean = true;
}
@Component({template: `<md-input (focus)="onFocus($event)" (blur)="onBlur($event)"></md-input>`})
class MdInputWithBlurAndFocusEvents {
onBlur(event: FocusEvent) {}
onFocus(event: FocusEvent) {}
}
@Component({template: `<md-input name="some-name"></md-input>`})
class MdInputWithNameTestController { }
@Component({template: `<md-input [autocomplete]="autoComplete"></md-input>`})
class MdInputWithAutocomplete { }
@Component({template: `<md-input autocomplete></md-input>`})
class MdInputWithUnboundAutocomplete { }
@Component({template: `<md-input autocomplete="name"></md-input>`})
class MdInputWithUnboundAutocompleteWithValue { }
@Component({template: `<md-input [autocorrect]="autoCorrect"></md-input>`})
class MdInputWithAutocorrect { }
@Component({template: `<md-input autocorrect></md-input>`})
class MdInputWithUnboundAutocorrect { }
@Component({template: `<md-input [autocapitalize]="autoCapitalize"></md-input>`})
class MdInputWithAutocapitalize { }
@Component({template: `<md-input autocapitalize></md-input>`})
class MdInputWithUnboundAutocapitalize { }
@Component({template: `<md-input [autofocus]="autoFocus"></md-input>`})
class MdInputWithAutofocus { }
@Component({template: `<md-input autofocus></md-input>`})
class MdInputWithUnboundAutofocus { }
@Component({template: `<md-input [readonly]="readOnly"></md-input>`})
class MdInputWithReadonly { }
@Component({template: `<md-input readonly></md-input>`})
class MdInputWithUnboundReadonly { }
@Component({template: `<md-input [spellcheck]="spellcheck"></md-input>`})
class MdInputWithSpellcheck { }
@Component({template: `<md-input spellcheck></md-input>`})
class MdInputWithUnboundSpellcheck { }
@Component({template: `<md-input [disabled]="disabled"></md-input>`})
class MdInputWithDisabled {
disabled: boolean;
}
@Component({template: `<md-input disabled></md-input>`})
class MdInputWithUnboundDisabled { }
@Component({template: `<md-input [required]="required"></md-input>`})
class MdInputWithRequired { }
@Component({template: `<md-input required></md-input>`})
class MdInputWithUnboundRequired { }
@Component({template: `<md-input [list]="list"></md-input>`})
class MdInputWithList { }
@Component({template: `<md-input [max]="max"></md-input>`})
class MdInputWithMax { }
@Component({template: `<md-input [min]="min"></md-input>`})
class MdInputWithMin { }
@Component({template: `<md-input [step]="step"></md-input>`})
class MdInputWithStep { }
@Component({template: `<md-input [tabindex]="tabIndex"></md-input>`})
class MdInputWithTabindex { }
@Component({template: `<md-input type="date" [placeholder]="placeholder"></md-input>`})
class MdInputDateTestController {
placeholder: string = '';
}
@Component({template: `<md-input type="text" [placeholder]="placeholder"></md-input>`})
class MdInputTextTestController {
placeholder: string = '';
}
@Component({template: `<md-input type="password" [placeholder]="placeholder"></md-input>`})
class MdInputPasswordTestController {
placeholder: string = '';
}
@Component({template: `<md-input type="number" [placeholder]="placeholder"></md-input>`})
class MdInputNumberTestController {
placeholder: string = '';
}
@Component({template: `<md-input [formControl]="formControl"></md-input>`})
class MdInputWithFormControl {
formControl = new FormControl();
}
@Component({template:
`<md-textarea [rows]="rows" [cols]="cols" [wrap]="wrap" placeholder="Snacks"></md-textarea>`})
class MdTextareaWithBindings {
rows: number = 4;
cols: number = 8;
wrap: string = 'hard';
}
-440
View File
@@ -1,440 +0,0 @@
import {
forwardRef,
Component,
HostBinding,
Input,
AfterContentInit,
ContentChild,
SimpleChange,
ContentChildren,
ViewChild,
ElementRef,
Renderer,
QueryList,
OnChanges,
EventEmitter,
Output,
NgModule,
ModuleWithProviders,
ViewEncapsulation
} from '@angular/core';
import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/forms';
import {CommonModule} from '@angular/common';
import {MdError, coerceBooleanProperty} from '../core';
import {Observable} from 'rxjs/Observable';
import {MdPlaceholder, MdInputContainer, MdHint, MdInputDirective} from './input-container';
import {MdTextareaAutosize} from './autosize';
import {PlatformModule} from '../core/platform/index';
const noop = () => {};
export const MD_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MdInput),
multi: true
};
// Invalid input type. Using one of these will throw an MdInputUnsupportedTypeError.
const MD_INPUT_INVALID_INPUT_TYPE = [
'file',
'radio',
'checkbox',
];
let nextUniqueId = 0;
/** @docs-private */
export class MdInputPlaceholderConflictError extends MdError {
constructor() {
super('Placeholder attribute and child element were both specified.');
}
}
/** @docs-private */
export class MdInputUnsupportedTypeError extends MdError {
constructor(type: string) {
super(`Input type "${type}" isn't supported by md-input.`);
}
}
/** @docs-private */
export class MdInputDuplicatedHintError extends MdError {
constructor(align: string) {
super(`A hint was already declared for 'align="${align}"'.`);
}
}
/**
* Component that represents a text input. It encapsulates the <input> HTMLElement and
* improve on its behaviour, along with styling it according to the Material Design.
* @deprecated
* @docs-private
*/
@Component({
moduleId: module.id,
selector: 'md-input, md-textarea',
templateUrl: 'input.html',
styleUrls: ['input.css'],
providers: [MD_INPUT_CONTROL_VALUE_ACCESSOR],
host: {'(click)' : 'focus()'},
encapsulation: ViewEncapsulation.None,
})
export class MdInput implements ControlValueAccessor, AfterContentInit, OnChanges {
private _focused: boolean = false;
private _value: any = '';
/** Callback registered via registerOnTouched (ControlValueAccessor) */
private _onTouchedCallback: () => void = noop;
/** Callback registered via registerOnChange (ControlValueAccessor) */
private _onChangeCallback: (_: any) => void = noop;
/**
* Aria related inputs.
*/
@Input('aria-label') ariaLabel: string;
@Input('aria-labelledby') ariaLabelledBy: string;
private _ariaDisabled: boolean;
private _ariaRequired: boolean;
private _ariaInvalid: boolean;
/** Mirrors the native `aria-disabled` attribute. */
@Input('aria-disabled')
get ariaDisabled(): boolean { return this._ariaDisabled; }
set ariaDisabled(value) { this._ariaDisabled = coerceBooleanProperty(value); }
/** Mirrors the native `aria-required` attribute. */
@Input('aria-required')
get ariaRequired(): boolean { return this._ariaRequired; }
set ariaRequired(value) { this._ariaRequired = coerceBooleanProperty(value); }
/** Mirrors the native `aria-invalid` attribute. */
@Input('aria-invalid')
get ariaInvalid(): boolean { return this._ariaInvalid; }
set ariaInvalid(value) { this._ariaInvalid = coerceBooleanProperty(value); }
/**
* Content directives.
*/
@ContentChild(MdPlaceholder) _placeholderChild: MdPlaceholder;
@ContentChildren(MdHint) _hintChildren: QueryList<MdHint>;
/** Readonly properties. */
/** Whether the element is focused. */
get focused() { return this._focused; }
/** Whether the element is empty. */
get empty() { return (this._value == null || this._value === '') && this.type !== 'date'; }
/** Amount of characters inside the element. */
get characterCount(): number {
return this.empty ? 0 : ('' + this._value).length;
}
/** Unique element id. */
get inputId(): string { return `${this.id}-input`; }
/** Alignment of the input container's content. */
@Input() align: 'start' | 'end' = 'start';
/** Color of the input divider, based on the theme. */
@Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary';
/** Text for the input hint. */
@Input() hintLabel: string = '';
/** Mirrors the native `autocomplete` attribute. */
@Input() autocomplete: string;
/** Mirrors the native `autocorrect` attribute. */
@Input() autocorrect: string;
/** Mirrors the native `autocapitalize` attribute. */
@Input() autocapitalize: string;
/** Unique id for the input element. */
@Input() id: string = `md-input-${nextUniqueId++}`;
/** Mirrors the native `list` attribute. */
@Input() list: string = null;
/** Mirrors the native `max` attribute. */
@Input() max: string | number = null;
/** Mirrors the native `maxlength` attribute. */
@Input() maxlength: number = null;
/** Mirrors the native `min` attribute. */
@Input() min: string | number = null;
/** Mirrors the native `minlength` attribute. */
@Input() minlength: number = null;
/** Mirrors the native `placeholder` attribute. */
@Input() placeholder: string = null;
/** Mirrors the native `step` attribute. */
@Input() step: number = null;
/** Mirrors the native `tabindex` attribute. */
@Input() tabindex: number = null;
/** Mirrors the native `type` attribute. */
@Input() type: string = 'text';
/** Mirrors the native `name` attribute. */
@Input() name: string = null;
// textarea-specific
/** Mirrors the native `rows` attribute. */
@Input() rows: number = null;
/** Mirrors the native `cols` attribute. */
@Input() cols: number = null;
/** Whether to do a soft or hard wrap of the text.. */
@Input() wrap: 'soft' | 'hard' = null;
private _floatingPlaceholder: boolean = true;
private _autofocus: boolean = false;
private _disabled: boolean = false;
private _readonly: boolean = false;
private _required: boolean = false;
private _spellcheck: boolean = false;
/** Text for the floating placeholder. */
@Input()
get floatingPlaceholder(): boolean { return this._floatingPlaceholder; }
set floatingPlaceholder(value) { this._floatingPlaceholder = coerceBooleanProperty(value); }
/** Whether to automatically focus the input. */
@Input()
get autofocus(): boolean { return this._autofocus; }
set autofocus(value) { this._autofocus = coerceBooleanProperty(value); }
/** Whether the input is disabled. */
@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value) { this._disabled = coerceBooleanProperty(value); }
/** Whether the input is readonly. */
@Input()
get readonly(): boolean { return this._readonly; }
set readonly(value) { this._readonly = coerceBooleanProperty(value); }
/** Whether the input is required. */
@Input()
get required(): boolean { return this._required; }
set required(value) { this._required = coerceBooleanProperty(value); }
/** Whether spellchecking is enable on the input. */
@Input()
get spellcheck(): boolean { return this._spellcheck; }
set spellcheck(value) { this._spellcheck = coerceBooleanProperty(value); }
private _blurEmitter: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();
private _focusEmitter: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();
/** Event emitted when the input is blurred. */
@Output('blur')
get onBlur(): Observable<FocusEvent> {
return this._blurEmitter.asObservable();
}
/** Event emitted when the input is focused. */
@Output('focus')
get onFocus(): Observable<FocusEvent> {
return this._focusEmitter.asObservable();
}
/** Value of the input. */
@Input()
get value(): any { return this._value; };
set value(v: any) {
v = this._convertValueForInputType(v);
if (v !== this._value) {
this._value = v;
this._onChangeCallback(v);
}
}
// This is to remove the `align` property of the `md-input` itself. Otherwise HTML5
// might place it as RTL when we don't want to. We still want to use `align` as an
// Input though, so we use HostBinding.
@HostBinding('attr.align') get _align(): any { return null; }
@ViewChild('input') _inputElement: ElementRef;
_elementType: 'input' | 'textarea';
constructor(elementRef: ElementRef, private _renderer: Renderer) {
// Set the element type depending on normalized selector used(md-input / md-textarea)
this._elementType = elementRef.nativeElement.nodeName.toLowerCase() === 'md-input' ?
'input' :
'textarea';
}
/** Set focus on input */
focus() {
this._renderer.invokeElementMethod(this._inputElement.nativeElement, 'focus');
}
_handleFocus(event: FocusEvent) {
this._focused = true;
this._focusEmitter.emit(event);
}
_handleBlur(event: FocusEvent) {
this._focused = false;
this._onTouchedCallback();
this._blurEmitter.emit(event);
}
_handleChange(event: Event) {
this.value = (<HTMLInputElement>event.target).value;
this._onTouchedCallback();
}
_hasPlaceholder(): boolean {
return !!this.placeholder || this._placeholderChild != null;
}
/**
* Sets the model value of the input. Implemented as part of ControlValueAccessor.
* @param value Value to be set.
*/
writeValue(value: any) {
this._value = value;
}
/**
* Registers a callback to be triggered when the input value has changed.
* Implemented as part of ControlValueAccessor.
* @param fn Callback to be registered.
*/
registerOnChange(fn: any) {
this._onChangeCallback = fn;
}
/**
* Registers a callback to be triggered when the input has been touched.
* Implemented as part of ControlValueAccessor.
* @param fn Callback to be registered.
*/
registerOnTouched(fn: any) {
this._onTouchedCallback = fn;
}
/**
* Sets whether the input is disabled.
* Implemented as a part of ControlValueAccessor.
* @param isDisabled Whether the input should be disabled.
*/
setDisabledState(isDisabled: boolean) {
this.disabled = isDisabled;
}
ngAfterContentInit() {
this._validateConstraints();
// Trigger validation when the hint children change.
this._hintChildren.changes.subscribe(() => {
this._validateConstraints();
});
}
ngOnChanges(changes: {[key: string]: SimpleChange}) {
this._validateConstraints();
}
/**
* Convert the value passed in to a value that is expected from the type of the md-input.
* This is normally performed by the *_VALUE_ACCESSOR in forms, but since the type is bound
* on our internal input it won't work locally.
* @private
*/
private _convertValueForInputType(v: any): any {
switch (this.type) {
case 'number': return parseFloat(v);
default: return v;
}
}
/**
* Ensure that all constraints defined by the API are validated, or throw errors otherwise.
* Constraints for now:
* - placeholder attribute and <md-placeholder> are mutually exclusive.
* - type attribute is not one of the forbidden types (see constant at the top).
* - Maximum one of each `<md-hint>` alignment specified, with the attribute being
* considered as align="start".
* @private
*/
private _validateConstraints() {
if (this.placeholder != '' && this.placeholder != null && this._placeholderChild != null) {
throw new MdInputPlaceholderConflictError();
}
if (MD_INPUT_INVALID_INPUT_TYPE.indexOf(this.type) != -1) {
throw new MdInputUnsupportedTypeError(this.type);
}
if (this._hintChildren) {
// Validate the hint labels.
let startHint: MdHint = null;
let endHint: MdHint = null;
this._hintChildren.forEach((hint: MdHint) => {
if (hint.align == 'start') {
if (startHint || this.hintLabel) {
throw new MdInputDuplicatedHintError('start');
}
startHint = hint;
} else if (hint.align == 'end') {
if (endHint) {
throw new MdInputDuplicatedHintError('end');
}
endHint = hint;
}
});
}
}
}
@NgModule({
declarations: [
MdInput,
MdPlaceholder,
MdInputContainer,
MdHint,
MdTextareaAutosize,
MdInputDirective
],
imports: [
CommonModule,
FormsModule,
PlatformModule,
],
exports: [
MdInput,
MdPlaceholder,
MdInputContainer,
MdHint,
MdTextareaAutosize,
MdInputDirective
],
})
export class MdInputModule {
/** @deprecated */
static forRoot(): ModuleWithProviders {
return {
ngModule: MdInputModule,
providers: [],
};
}
}