Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(input): add directive for displaying error messages #3560

Merged
merged 7 commits into from
Mar 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/demo-app/input/input-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,51 @@
</md-card-content>
</md-card>

<md-card class="demo-card demo-basic">
<md-toolbar color="primary">Error messages</md-toolbar>
<md-card-content>
<h4>Regular</h4>

<p>
<md-input-container>
<input mdInput placeholder="example" [(ngModel)]="errorMessageExample1" required>
<md-error>This field is required</md-error>
</md-input-container>

<md-input-container>
<input mdInput placeholder="email" [formControl]="emailFormControl">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the error animation is kind of strange in this case, the error message seems to appear in the middle of the input and then move down (I think because the message wraps on to 2 lines).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take another look. It probably needs a transform-origin.

<md-error *ngIf="emailFormControl.hasError('required')">
This field is required
</md-error>
<md-error *ngIf="emailFormControl.hasError('pattern')">
Please enter a valid email address
</md-error>
</md-input-container>
</p>

<h4>With hint</h4>

<md-input-container>
<input mdInput placeholder="example" [(ngModel)]="errorMessageExample2" required>
<md-error>This field is required</md-error>
<md-hint>Please type something here</md-hint>
</md-input-container>


<form novalidate>
<h4>Inside a form</h4>

<md-input-container>
<input mdInput name="example" placeholder="example"
[(ngModel)]="errorMessageExample3" required>
<md-error>This field is required</md-error>
</md-input-container>

<button color="primary" md-raised-button>Submit</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this button supposed to do something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's used to test out that form submission shows the error messages.

</form>
</md-card-content>
</md-card>

<md-card class="demo-card demo-basic">
<md-toolbar color="primary">Prefix + Suffix</md-toolbar>
<md-card-content>
Expand Down
6 changes: 6 additions & 0 deletions src/demo-app/input/input-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {FormControl, Validators} from '@angular/forms';

let max = 5;

const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

@Component({
moduleId: module.id,
selector: 'input-demo',
Expand All @@ -17,6 +19,9 @@ export class InputDemo {
ctrlDisabled = false;

name: string;
errorMessageExample1: string;
errorMessageExample2: string;
errorMessageExample3: string;
items: any[] = [
{ value: 10 },
{ value: 20 },
Expand All @@ -26,6 +31,7 @@ export class InputDemo {
];
rows = 8;
formControl = new FormControl('hello', Validators.required);
emailFormControl = new FormControl('', [Validators.required, Validators.pattern(EMAIL_REGEX)]);
model = 'hello';

addABunch(n: number) {
Expand Down
7 changes: 6 additions & 1 deletion src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {TestBed, async, fakeAsync, tick, ComponentFixture} from '@angular/core/testing';
import {Component, OnDestroy, QueryList, ViewChild, ViewChildren} from '@angular/core';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MdAutocompleteModule, MdAutocompleteTrigger} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
import {MdInputModule} from '../input/index';
Expand All @@ -27,7 +28,11 @@ describe('MdAutocomplete', () => {
dir = 'ltr';
TestBed.configureTestingModule({
imports: [
MdAutocompleteModule.forRoot(), MdInputModule.forRoot(), FormsModule, ReactiveFormsModule
MdAutocompleteModule.forRoot(),
MdInputModule.forRoot(),
FormsModule,
ReactiveFormsModule,
NoopAnimationsModule
],
declarations: [
SimpleAutocomplete,
Expand Down
6 changes: 4 additions & 2 deletions src/lib/core/compatibility/compatibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ export const MAT_ELEMENTS_SELECTOR = `
mat-spinner,
mat-tab,
mat-tab-group,
mat-toolbar`;
mat-toolbar,
mat-error`;

/** Selector that matches all elements that may have style collisions with AngularJS Material. */
export const MD_ELEMENTS_SELECTOR = `
Expand Down Expand Up @@ -130,7 +131,8 @@ export const MD_ELEMENTS_SELECTOR = `
md-spinner,
md-tab,
md-tab-group,
md-toolbar`;
md-toolbar,
md-error`;

/** Directive that enforces that the `mat-` prefix cannot be used. */
@Directive({selector: MAT_ELEMENTS_SELECTOR})
Expand Down
17 changes: 14 additions & 3 deletions src/lib/input/_input-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
$warn: map-get($theme, warn);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);

// Placeholder colors. Required is used for the `*` star shown in the placeholder.
$input-placeholder-color: mat-color($foreground, hint-text);
$input-floating-placeholder-color: mat-color($primary);
$input-required-placeholder-color: mat-color($accent);

// Underline colors.
$input-underline-color: mat-color($foreground, divider);
$input-underline-color-accent: mat-color($accent);
Expand Down Expand Up @@ -64,7 +64,10 @@
}
}

.mat-input-container.ng-invalid.ng-touched:not(.mat-focused) {
// Styling for the error state of the input container. Note that while the same can be
// achieved with the ng-* classes, we use this approach in order to ensure that the same
// logic is used to style the error state and to show the error messages.
.mat-input-invalid {
.mat-input-placeholder,
.mat-placeholder-required {
color: $input-underline-color-warn;
Expand All @@ -73,5 +76,13 @@
.mat-input-underline {
border-color: $input-underline-color-warn;
}

.mat-input-ripple {
background-color: $input-underline-color-warn;
}
}

.mat-input-error {
color: $input-underline-color-warn;
}
}
14 changes: 11 additions & 3 deletions src/lib/input/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import {NgModule, ModuleWithProviders} from '@angular/core';
import {MdPlaceholder, MdInputContainer, MdHint, MdInputDirective} from './input-container';
import {
MdPlaceholder,
MdInputContainer,
MdHint,
MdInputDirective,
MdErrorDirective,
} from './input-container';
import {MdTextareaAutosize} from './autosize';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
Expand All @@ -12,7 +18,8 @@ import {PlatformModule} from '../core/platform/index';
MdInputContainer,
MdHint,
MdTextareaAutosize,
MdInputDirective
MdInputDirective,
MdErrorDirective
],
imports: [
CommonModule,
Expand All @@ -24,7 +31,8 @@ import {PlatformModule} from '../core/platform/index';
MdInputContainer,
MdHint,
MdTextareaAutosize,
MdInputDirective
MdInputDirective,
MdErrorDirective
],
})
export class MdInputModule {
Expand Down
13 changes: 11 additions & 2 deletions src/lib/input/input-container.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
[class.mat-warn]="dividerColor == 'warn'"></span>
</div>

<div *ngIf="hintLabel != ''" [attr.id]="_hintLabelId" class="mat-hint">{{hintLabel}}</div>
<ng-content select="md-hint, mat-hint"></ng-content>
<div class="mat-input-subscript-wrapper" [ngSwitch]="_getDisplayedMessages()">
<div *ngSwitchCase="'error'" [@transitionMessages]="_subscriptAnimationState">
<ng-content select="md-error, mat-error"></ng-content>
</div>

<div class="mat-input-hint-wrapper" *ngSwitchCase="'hint'"
[@transitionMessages]="_subscriptAnimationState">
<div *ngIf="hintLabel" [id]="_hintLabelId" class="mat-hint">{{hintLabel}}</div>
<ng-content select="md-hint, mat-hint"></ng-content>
</div>
</div>
</div>
51 changes: 40 additions & 11 deletions src/lib/input/input-container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


$mat-input-floating-placeholder-scale-factor: 0.75 !default;
$mat-input-wrapper-spacing: 1em !default;

// Gradient for showing the dashed line when the input is disabled.
$mat-input-underline-disabled-background-image:
Expand Down Expand Up @@ -41,7 +42,7 @@ $mat-input-underline-disabled-background-image:
// Global wrapper. We need to apply margin to the element for spacing, but
// cannot apply it to the host element directly.
.mat-input-wrapper {
margin: 1em 0;
margin: $mat-input-wrapper-spacing 0;
// Account for the underline which has 4px of margin + 2px of border.
padding-bottom: 6px;
}
Expand Down Expand Up @@ -219,29 +220,57 @@ $mat-input-underline-disabled-background-image:
}
}

// The hint is shown below the underline. There can be more than one; one at the start
// and one at the end.
.mat-hint {
display: block;
// Wrapper for the hints and error messages. Provides positioning and text size.
// Note that we're using `top` in order to allow for stacked children to flow downwards.
.mat-input-subscript-wrapper {
position: absolute;
font-size: 75%;
bottom: 0;
top: 100%;
width: 100%;
margin-top: -$mat-input-wrapper-spacing;
overflow: hidden; // prevents multi-line errors from overlapping the input
}

// Clears the floats on the hints. This is necessary for the hint animation to work.
.mat-input-hint-wrapper {
&::before,
&::after {
content: ' ';
display: table;
}

&::after {
clear: both;
}
}

// The hint is shown below the underline. There can be
// more than one; one at the start and one at the end.
.mat-hint {
display: block;
float: left;

// We use floats here, as opposed to flexbox, in order to make it
// easier to reverse their location in rtl and to ensure that they're
// aligned properly in some cases (e.g. when there is only an `end` hint).
&.mat-right {
right: 0;
float: right;
}

[dir='rtl'] & {
right: 0;
left: auto;
float: right;

&.mat-right {
right: auto;
left: 0;
float: left;
}
}
}

// Single error message displayed beneath the input.
.mat-input-error {
display: block;
}

.mat-input-prefix, .mat-input-suffix {
// Prevents the prefix and suffix from stretching together with the container.
width: 0.1px;
Expand Down
Loading