mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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:
@@ -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>
|
||||
|
||||
@@ -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 = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>$ </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: </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> </span>
|
||||
<span mdSuffix class="demo-transform" [class.primary]="email.focused"> @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>
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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
@@ -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: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
`
|
||||
})
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
@@ -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: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user