docs(form-field): add docs, examples, and guide (#7424)

This commit is contained in:
mmalerba
2017-10-04 15:46:10 -07:00
committed by Kara
parent c2c6e04ed6
commit adf9b5b978
26 changed files with 936 additions and 1 deletions
@@ -0,0 +1,337 @@
It is possible to create custom form field controls that can be used inside `<mat-form-field>`. This
can be useful if you need to create a component that shares a lot of common behavior with a form
field, but adds some additional logic.
For example in this guide we'll learn how to create a custom input for inputting US telephone
numbers and hook it up to work with `<mat-form-field>`. Here is what we'll build by the end of this
guide:
<!-- example(form-field-custom-control) -->
In order to learn how to build custom form field controls, let's start with a simple input component
that we want to work inside the form field. For example, a phone number input that segments the
parts of the number into their own inputs. (Note: this is not intended to be a robust component,
just a starting point for us to learn.)
```ts
class MyTel {
constructor(public area: string, public exchange: string, public subscriber: string) {}
}
@Component({
selector: 'my-tel-input',
template: `
<div [formGroup]="parts">
<input class="area" formControlName="area" size="3">
<span>&ndash;</span>
<input class="exchange" formControlName="exchange" size="3">
<span>&ndash;</span>
<input class="subscriber" formControlName="subscriber" size="4">
</div>
`,
styles: [`
div {
display: flex;
}
input {
border: none;
background: none;
padding: 0;
outline: none;
font: inherit;
text-align: center;
}
`],
})
class MyTelInput {
parts: FormGroup;
@Input()
get value(): MyTel | null {
let n = this.parts.value;
if (n.area.length == 3 && n.exchange.length == 3 && n.subscriber.length == 4) {
return new MyTel(n.area, n.exchange, n.subscriber);
}
return null;
}
set value(tel: MyTel | null) {
tel = tel || new MyTel('', '', '');
this.parts.setValue({area: tel.area, exchange: tel.exchange, subscriber: tel.subscriber});
}
constructor(fb: FormBuilder) {
this.parts = fb.group({
'area': '',
'exchange': '',
'subscriber': '',
});
}
}
```
### Providing our component as a MatFormFieldControl
The first step is to provide our new component as an implementation of the `MatFormFieldControl`
interface that the `<mat-form-field>` knows how to work with. To do this, we will have our class
implement `MatFormFieldControl`. Since this is a generic interface, we'll need to include a type
parameter indicating the type of data our control will work with, in this case `MyTel`. We then add
a provider to our component so that the form field will be able to inject it as a
`MatFormFieldControl`.
```ts
@Component({
...
providers: [{provide: MatFormFieldControl, useExisting: MyTelInput}],
})
class MyTelInput implements MatFormFieldControl<MyTel> {
...
}
```
This sets up our component so it can work with `<mat-form-field>`, but now we need to implement the
various methods and properties declared by the interface we just implemented. To learn more about
the `MatFormFieldControl` interface, see its
[definition](https://github.com/angular/material2/blob/master/src/lib/form-field/form-field-control.ts).
(Unfortunately generated API docs are not available yet, but we'll go through the methods and
properties in this guide.)
### Implementing the methods and properties of MatFormFieldControl
#### `value`
This property allows someone to set or get the value of our control. Its type should be the same
type we used for the type parameter when we implemented `MatFormFieldControl`. Since our component
already has a value property, we don't need to do anything for this one.
#### `stateChanges`
Because the `<mat-form-field>` uses the `OnPush` change detection strategy, we need to let it know
when something happens in the form field control that may require the form field to run change
detection. We do this via the `stateChanges` property. So far the only thing the form field needs to
know about is when the value changes. We'll need to emit on the stateChanges stream when that
happens, and as we continue flushing out these properties we'll likely find more places we need to
emit. We should also make sure to complete `stateChanges` when our component is destroyed.
```ts
stateChanges = new Subject<void>();
set value(tel: MyTel | null) {
...
this.stateChanges.next();
}
ngOnDestroy() {
this.stateChanges.complete();
}
```
#### `id`
This property should return the ID of an element in the component's template that we want the
`<mat-form-field>` to associate all of its labels and hints with. In this case, we'll use the host
element and just generate a unique ID for it.
```ts
static nextId = 0;
@HostBinding() id = `my-tel-input-${MyTelInput.nextId++}`;
```
#### `placeholder`
This property allows us to tell the `<mat-form-field>` what to use as a placeholder. In this
example, we'll do the same thing as `matInput` and `<mat-select>` and allow the user to specify it
via an `@Input()`. Since the value of the placeholder may change over time, we need to make sure to
trigger change detection in the parent form field by emitting on the `stateChanges` stream when the
placeholder changes.
```ts
@Input()
get placeholder() {
return this._placeholder;
}
set placeholder(plh) {
this._placeholder = plh;
this.stateChanges.next();
}
private _placeholder: string;
```
#### `ngControl`
This property allows the form field control to specify the `@angular/forms` control that is bound to
this component. Since we haven't set up our component to act as a `ControlValueAccessor`, we'll just
set this to `null` in our component. In any real component, you would probably want to implement
`ControlValueAccessor` so that your component can work with `formControl` and `ngModel`.
```ts
ngControl = null;
```
If you did implement `ControlValueAccessor`, you could simply inject the `NgControl` and make it
publicly available. (For additional information about `ControlValueAccessor` see the
[API docs](https://angular.io/api/forms/ControlValueAccessor).)
```ts
constructor(..., @Optional() @Self() public ngControl: NgControl) { ... }
```
#### `focused`
This property indicates whether or not the form field control should be considered to be in a
focused state. When it is in a focused state, the form field is displayed with a solid color
underline. For the purposes of our component, we want to consider it focused if any of the part
inputs are focused. We can use the `FocusMonitor` from `@angular/cdk` to easily check this. We also
need to remember to emit on the `stateChanges` stream so change detection can happen.
```ts
focused = false;
constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef,
renderer: Renderer2) {
...
fm.monitor(elRef.nativeElement, renderer, true).subscribe(origin => {
this.focused = !!origin;
this.stateChanges.next();
});
}
ngOnDestroy() {
...
this.fm.stopMonitoring(this.elRef.nativeElement);
}
```
#### `empty`
This property indicates whether the form field control is empty. For our control, we'll consider it
empty if all of the parts are empty.
```ts
get empty() {
let n = this.parts.value;
return !n.area && !n.exchange && !n.subscriber;
}
```
#### `shouldPlaceholderFloat`
This property is used to indicate whether the placeholder should be in the floating position. We'll
use the same logic as `matInput` and float the placeholder when the input is focused or non-empty.
Since the placeholder will be overlapping our control when when it's not floating, we should hide
the `–` characters when it's not floating.
```ts
@HostBinding('class.floating')
get shouldPlaceholderFloat() {
return this.focused || !this.empty;
}
```
```css
span {
opacity: 0;
transition: opacity 200ms;
}
:host.floating span {
opacity: 1;
}
```
#### `required`
This property is used to indicate whether the input is required. `<mat-form-field>` uses this
information to add a required indicator to the placeholder. Again, we'll want to make sure we run
change detection if the required state changes.
```ts
@Input()
get required() {
return this._required;
}
set required(req) {
this._required = coerceBooleanProperty(req);
this.stateChanges.next();
}
private _required = false;
```
#### `disabled`
This property tells the form field when it should be in the disabled state. In addition to reporting
the right state to the form field, we need to set the disabled state on the individual inputs that
make up our component.
```ts
@Input()
get disabled() {
return this._disabled;
}
set disabled(dis) {
this._disabled = coerceBooleanProperty(dis);
this.stateChanges.next();
}
private _disabled = false;
```
```html
<input class="area" formControlName="area" size="3" [disabled]="disabled">
<span>&ndash;</span>
<input class="exchange" formControlName="exchange" size="3" [disabled]="disabled">
<span>&ndash;</span>
<input class="subscriber" formControlName="subscriber" size="4" [disabled]="disabled">
```
#### `errorState`
This property indicates whether the associated `NgControl` is in an error state. Since we're not
using an `NgControl` in this example, we don't need to do anything other than just set it to `false`.
```ts
errorState = false;
```
#### `controlType`
This property allows us to specify a unique string for the type of control in form field. The
`<mat-form-field>` will add an additional class based on this type that can be used to easily apply
special styles to a `<mat-form-field>` that contains a specific type of control. In this example
we'll use `my-tel-input` as our control type which will result in the form field adding the class
`mat-form-field-my-tel-input`.
```ts
controlType = 'my-tel-input';
```
#### `setAriaDescribedByIds(ids: string[])`
This method is used by the `<mat-form-field>` to specify the IDs that should be used for the
`aria-describedby` attribute of your component. The method has one parameter, the list of IDs, we
just need to apply the given IDs to our host element.
```ts
@HostBinding('attr.aria-describedby') describedBy = '';
setDescribedByIds(ids: string[]) {
this.describedBy = ids.join(' ');
}
```
#### `onContainerClick(event: MouseEvent)`
This method will be called when the form field is clicked on. It allows your component to hook in
and handle that click however it wants. The method has one parameter, the `MouseEvent` for the
click. In our case we'll just focus the first `<input>` if the user isn't about to click an
`<input>` anyways.
```ts
onContainerClick(event: MouseEvent) {
if ((event.target as Element).tagName.toLowerCase() != 'input') {
this.elRef.nativeElement.querySelector('input').focus();
}
}
```
### Trying it out
Now that we've fully implemented the interface, we're ready to try our component out! All we need to
do is place it inside of a `<mat-form-field>`
```html
<mat-form-field>
<my-tel-input></my-tel-input>
</mat-form-field>
```
We also get all of the features that come with `<mat-form-field>` such as floating placeholder,
prefix, suffix, hints, and errors (if we've given the form field an `NgControl` and correctly report
the error state).
```html
<mat-form-field>
<my-tel-input placeholder="Phone number" required></my-tel-input>
<mat-icon matPrefix>phone</mat-icon>
<mat-hint>Include area code</mat-hint>
</mat-form-field>
```
+1
View File
@@ -0,0 +1 @@
Please see the official documentation at https://material.angular.io/components/component/form-field
+1 -1
View File
@@ -13,7 +13,7 @@ import {NgControl} from '@angular/forms';
/** An interface which allows a control to work inside of a `MatFormField`. */
export abstract class MatFormFieldControl<T> {
/** The value of the control. */
value: T;
value: T | null;
/**
* Stream that emits whenever the state of the control changes such that the parent `MatFormField`
+129
View File
@@ -0,0 +1,129 @@
`<mat-form-field>` is a component used to wrap several Angular Material components and apply common
[Text field](https://material.io/guidelines/components/text-fields.html) styles such as the
underline, floating label, and hint messages.
In this document, "form field" refers to the wrapper component `<mat-form-field>` and
"form field control" refers to the component that the `<mat-form-field>` is wrapping
(e.g. the input, textarea, select, etc.)
The following Angular Material components are designed to work inside a `<mat-form-field>`:
* [&lt;input matInput&gt; &amp; &lt;textarea matInput&gt;](https://material.angular.io/components/input/overview)
* [&lt;mat-select&gt;](https://material.angular.io/components/select/overview)
* [&lt;mat-chip-list&gt;](https://material.angular.io/components/chips/overview)
<!-- example(form-field-overview) -->
### Floating placeholder
The floating placeholder is a text label displayed on top of the form field control when
the control does not contain any text. By default, when text is present the floating placeholder
floats above the form field control.
Placeholder text can be specified using the `placeholder` property on the form field control, or
by adding a `<mat-placeholder>` element inside the form field. Only one of these options should be
used, specifying both will raise an error.
If the form field control is marked with a `required` attribute, an asterisk will be appended to the
placeholder to indicate the fact that it is a required field. If unwanted, this can be disabled by
setting the `hideRequiredMarker` property on `<mat-form-field>`
The `floatPlaceholder` property of `<mat-form-field>` can be used to change this default floating
behavior. It can set to `never` to hide the placeholder instead of float it when text is present in
the form field control. It can be set to `always` to float the placeholder even when no text is
present in the form field control. It can also be set to `auto` to restore the default behavior.
<!-- example(form-field-placeholder) -->
Global default placeholder options can be specified by setting providing a value for
`MAT_PLACEHOLDER_GLOBAL_OPTIONS` in your application's root module. Like the property, the global
setting can be either `always`, `never`, or `auto`.
```ts
@NgModule({
providers: [
{provide: MAT_PLACEHOLDER_GLOBAL_OPTIONS, useValue: {float: 'always'}}
]
})
```
### Hint labels
Hint labels are additional descriptive text that appears below the form field's underline. A
`<mat-form-field>` can have up to two hint labels; one start-aligned (left in an LTR language, right
in RTL), and one end-aligned.
Hint labels are specified in one of two ways: either by using the `hintLabel` property of
`<mat-form-field>`, or by adding a `<mat-hint>` element inside the form field. When adding a hint
via the `hintLabel` property, it will be treated as the start hint. Hints added via the
`<mat-hint>` hint element can be added to either side by setting the `align` property on
`<mat-hint>` to either `start` or `end`. Attempting to add multiple hints to the same side will
raise an error.
<!-- example(form-field-hint) -->
### Error messages
Error messages can be shown under the form field underline by adding `mat-error` elements inside the
form field. Errors are hidden initially and will be displayed on invalid form fields after the user
has interacted with the element or the parent form has been submitted. Since the errors occupy the
same space as the hints, the hints are hidden when the errors are shown.
If a form field can have more than one error state, it is up to the consumer to toggle which
messages should be displayed. This can be done with CSS, `ngIf` or `ngSwitch`. Multiple error
messages can be shown at the same time if desired, but the `<mat-form-field>` only reserves enough
space to display one error message at a time. Ensuring that enough space is available to display
multiple errors is up to the user.
<!-- example(form-field-error) -->
### Prefix & suffix
Custom content can be included before and after the input tag, as a prefix or suffix. It will be
included within the visual container that wraps the form control as per the Material specification.
Adding the `matPrefix` directive to an element inside the `<mat-form-field>` will designate it as
the prefix. Similarly, adding `matSuffix` will designate it as the suffix.
<!-- example(form-field-prefix-suffix) -->
### Custom form field controls
In addition to the form field controls that Angular Material provides, it is possible to create
custom form field controls that work with `<mat-form-field>` in the same way. For additional
information on this see the guide on
[Creating Custom mat-form-field Controls](https://material.angular.io/guide/creating-a-custom-form-field-control).
### Theming
`<mat-form-field>` has a `color` property which can be set to `primary`, `accent`, or `warn`. This
will set the color of the form field underline and floating placeholder based on the theme colors
of your app.
`<mat-form-field>` inherits its `font-size` from its parent element. This can be overridden to an
explicit size using CSS. We recommend a specificity of at least 1 element + 1 class.
```css
mat-form-field.mat-form-field {
font-size: 16px;
}
```
<!-- example(form-field-theming) -->
### Accessibility
If a floating placeholder is specified, it will be automatically used as the label for the form field
control. If no floating placeholder is specified, the user should label the form field control
themselves using `aria-label`, `aria-labelledby` or `<label for=...>`.
Any errors and hints added to the form field are automatically added to the form field control's
`aria-describedby` set.
### Troubleshooting
#### Error: Placeholder attribute and child element were both specified
This error occurs when you have specified two conflicting placeholders. Make sure that you haven't
included both a `placeholder` property on your form field control and a `<mat-placeholder>`
element.
#### Error: A hint was already declared for align="..."
This error occurs if you have added multiple hints for the same side. Keep in mind that the
`hintLabel` property adds a hint to the start side.
#### Error: mat-form-field must contain a MatFormFieldControl
This error occurs when you have not added a form field control to your form field. If your form
field contains a native `<input>` or `<textarea>` element, make sure you've added the `matInput`
directive to it. Other components that can act as a form field control include `<mat-select>`,
`<mat-chip-list>`, and any custom form field controls you've created.
+56
View File
@@ -41,6 +41,13 @@ import {DialogElementsExampleDialog,DialogElementsExample} from './dialog-elemen
import {DialogOverviewExampleDialog,DialogOverviewExample} from './dialog-overview/dialog-overview-example';
import {ExpansionOverviewExample} from './expansion-overview/expansion-overview-example';
import {ExpansionStepsExample} from './expansion-steps/expansion-steps-example';
import {MyTelInput,FormFieldCustomControlExample} from './form-field-custom-control/form-field-custom-control-example';
import {FormFieldErrorExample} from './form-field-error/form-field-error-example';
import {FormFieldHintExample} from './form-field-hint/form-field-hint-example';
import {FormFieldOverviewExample} from './form-field-overview/form-field-overview-example';
import {FormFieldPlaceholderExample} from './form-field-placeholder/form-field-placeholder-example';
import {FormFieldPrefixSuffixExample} from './form-field-prefix-suffix/form-field-prefix-suffix-example';
import {FormFieldThemingExample} from './form-field-theming/form-field-theming-example';
import {GridListDynamicExample} from './grid-list-dynamic/grid-list-dynamic-example';
import {GridListOverviewExample} from './grid-list-overview/grid-list-overview-example';
import {IconOverviewExample} from './icon-overview/icon-overview-example';
@@ -260,6 +267,48 @@ export const EXAMPLE_COMPONENTS = {
additionalFiles: null,
selectorName: null
},
'form-field-custom-control': {
title: 'Form field with custom telephone number input control. ',
component: FormFieldCustomControlExample,
additionalFiles: ["form-field-custom-control-example.html"],
selectorName: 'FormFieldCustomControlExample, MyTelInput'
},
'form-field-error': {
title: 'Form field with error messages ',
component: FormFieldErrorExample,
additionalFiles: null,
selectorName: null
},
'form-field-hint': {
title: 'Form field with hints ',
component: FormFieldHintExample,
additionalFiles: null,
selectorName: null
},
'form-field-overview': {
title: 'Simple form field ',
component: FormFieldOverviewExample,
additionalFiles: null,
selectorName: null
},
'form-field-placeholder': {
title: 'Form field with placeholder ',
component: FormFieldPlaceholderExample,
additionalFiles: null,
selectorName: null
},
'form-field-prefix-suffix': {
title: 'Form field with prefix & suffix ',
component: FormFieldPrefixSuffixExample,
additionalFiles: null,
selectorName: null
},
'form-field-theming': {
title: 'Form field theming ',
component: FormFieldThemingExample,
additionalFiles: null,
selectorName: null
},
'grid-list-dynamic': {
title: 'Dynamic grid-list',
component: GridListDynamicExample,
@@ -585,6 +634,13 @@ export const EXAMPLE_LIST = [
DialogOverviewExampleDialog,DialogOverviewExample,
ExpansionOverviewExample,
ExpansionStepsExample,
MyTelInput,FormFieldCustomControlExample,
FormFieldErrorExample,
FormFieldHintExample,
FormFieldOverviewExample,
FormFieldPlaceholderExample,
FormFieldPrefixSuffixExample,
FormFieldThemingExample,
GridListDynamicExample,
GridListOverviewExample,
IconOverviewExample,
@@ -0,0 +1,21 @@
div {
display: flex;
}
input {
border: none;
background: none;
padding: 0;
outline: none;
font: inherit;
text-align: center;
}
span {
opacity: 0;
transition: opacity 200ms;
}
:host.floating span {
opacity: 1;
}
@@ -0,0 +1,7 @@
<div [formGroup]="parts">
<input class="area" formControlName="area" size="3" [disabled]="disabled">
<span>&ndash;</span>
<input class="exchange" formControlName="exchange" size="3" [disabled]="disabled">
<span>&ndash;</span>
<input class="subscriber" formControlName="subscriber" size="4" [disabled]="disabled">
</div>
@@ -0,0 +1,137 @@
import {FocusMonitor} from '@angular/cdk/a11y';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {Component, ElementRef, HostBinding, Input, OnDestroy, Renderer2} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {MatFormFieldControl} from '@angular/material/form-field';
import {Subject} from 'rxjs/Subject';
/** Data structure for holding telephone number. */
export class MyTel {
constructor(public area: string, public exchange: string, public subscriber: string) {}
}
/** Custom `MatFormFieldControl` for telephone number input. */
@Component({
selector: 'my-tel-input',
templateUrl: 'form-field-custom-control-example.html',
styleUrls: ['form-field-custom-control-example.css'],
providers: [{provide: MatFormFieldControl, useExisting: MyTelInput}],
})
export class MyTelInput implements MatFormFieldControl<MyTel>, OnDestroy {
static nextId = 0;
parts: FormGroup;
stateChanges = new Subject<void>();
focused = false;
ngControl = null;
errorState = false;
controlType = 'my-tel-input';
get empty() {
let n = this.parts.value;
return !n.area && !n.exchange && !n.subscriber;
}
@HostBinding('class.floating')
get shouldPlaceholderFloat() {
return this.focused || !this.empty;
}
@HostBinding() id = `my-tel-input-${MyTelInput.nextId++}`;
@HostBinding('attr.aria-describedby') describedBy = '';
@Input()
get placeholder() {
return this._placeholder;
}
set placeholder(plh) {
this._placeholder = plh;
this.stateChanges.next();
}
private _placeholder: string;
@Input()
get required() {
return this._required;
}
set required(req) {
this._required = coerceBooleanProperty(req);
this.stateChanges.next();
}
private _required = false;
@Input()
get disabled() {
return this._disabled;
}
set disabled(dis) {
this._disabled = coerceBooleanProperty(dis);
this.stateChanges.next();
}
private _disabled = false;
@Input()
get value(): MyTel | null {
let n = this.parts.value;
if (n.area.length == 3 && n.exchange.length == 3 && n.subscriber.length == 4) {
return new MyTel(n.area, n.exchange, n.subscriber);
}
return null;
}
set value(tel: MyTel | null) {
tel = tel || new MyTel('', '', '');
this.parts.setValue({area: tel.area, exchange: tel.exchange, subscriber: tel.subscriber});
this.stateChanges.next();
}
constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef,
renderer: Renderer2) {
this.parts = fb.group({
'area': '',
'exchange': '',
'subscriber': '',
});
fm.monitor(elRef.nativeElement, renderer, true).subscribe((origin) => {
this.focused = !!origin;
this.stateChanges.next();
});
}
ngOnDestroy() {
this.stateChanges.complete();
this.fm.stopMonitoring(this.elRef.nativeElement);
}
setDescribedByIds(ids: string[]) {
this.describedBy = ids.join(' ');
}
onContainerClick(event: MouseEvent) {
if ((event.target as Element).tagName.toLowerCase() != 'input') {
this.elRef.nativeElement.querySelector('input').focus();
}
}
}
/** @title Form field with custom telephone number input control. */
@Component({
selector: 'form-field-custom-control-example',
template: `
<mat-form-field>
<my-tel-input placeholder="Phone number" required></my-tel-input>
<mat-icon matSuffix>phone</mat-icon>
<mat-hint>Include area code</mat-hint>
</mat-form-field>
`
})
export class FormFieldCustomControlExample {}
@@ -0,0 +1,8 @@
.example-container {
display: flex;
flex-direction: column;
}
.example-container > * {
width: 100%;
}
@@ -0,0 +1,6 @@
<div class="example-container">
<mat-form-field>
<input matInput placeholder="Enter your email" [formControl]="email" required>
<mat-error *ngIf="email.invalid">{{getErrorMessage()}}</mat-error>
</mat-form-field>
</div>
@@ -0,0 +1,18 @@
import {Component} from '@angular/core';
import {FormControl, Validators} from '@angular/forms';
/** @title Form field with error messages */
@Component({
selector: 'form-field-error-example',
templateUrl: 'form-field-error-example.html',
styleUrls: ['form-field-error-example.css']
})
export class FormFieldErrorExample {
email = new FormControl('', [Validators.required, Validators.email]);
getErrorMessage() {
return this.email.hasError('required') ? 'You must enter a value' :
this.email.hasError('email') ? 'Not a valid email' :
'';
}
}
@@ -0,0 +1,8 @@
.example-container {
display: flex;
flex-direction: column;
}
.example-container > * {
width: 100%;
}
@@ -0,0 +1,13 @@
<div class="example-container">
<mat-form-field hintLabel="Max 10 characters">
<input matInput #input maxlength="10" placeholder="Enter some input">
<mat-hint align="end">{{input.value?.length || 0}}/10</mat-hint>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Select me">
<mat-option value="option">Option</mat-option>
</mat-select>
<mat-hint align="end">Here's the dropdown arrow ^</mat-hint>
</mat-form-field>
</div>
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';
/** @title Form field with hints */
@Component({
selector: 'form-field-hint-example',
templateUrl: 'form-field-hint-example.html',
styleUrls: ['form-field-hint-example.css']
})
export class FormFieldHintExample {}
@@ -0,0 +1,8 @@
.example-container {
display: flex;
flex-direction: column;
}
.example-container > * {
width: 100%;
}
@@ -0,0 +1,15 @@
<div class="example-container">
<mat-form-field>
<input matInput placeholder="Input">
</mat-form-field>
<mat-form-field>
<textarea matInput placeholder="Textarea"></textarea>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Select">
<mat-option value="option">Option</mat-option>
</mat-select>
</mat-form-field>
</div>
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';
/** @title Simple form field */
@Component({
selector: 'form-field-overview-example',
templateUrl: 'form-field-overview-example.html',
styleUrls: ['form-field-overview-example.css']
})
export class FormFieldOverviewExample {}
@@ -0,0 +1,20 @@
.example-container {
display: flex;
flex-direction: column;
}
.example-container > * {
width: 100%;
}
.example-container form {
margin-bottom: 20px;
}
.example-container form > * {
margin: 5px 0;
}
.example-container .mat-radio-button {
margin: 0 5px;
}
@@ -0,0 +1,29 @@
<div class="example-container">
<form class="example-container" [formGroup]="options">
<mat-checkbox formControlName="hideRequired">Hide required marker</mat-checkbox>
<div>
<label>Float placeholder: </label>
<mat-radio-group formControlName="floatPlaceholder">
<mat-radio-button value="auto">Auto</mat-radio-button>
<mat-radio-button value="always">Always</mat-radio-button>
<mat-radio-button value="never">Never</mat-radio-button>
</mat-radio-group>
</div>
</form>
<mat-form-field
[hideRequiredMarker]="options.value.hideRequired"
[floatPlaceholder]="options.value.floatPlaceholder">
<input matInput placeholder="Simple placeholder" required>
</mat-form-field>
<mat-form-field
[hideRequiredMarker]="options.value.hideRequired"
[floatPlaceholder]="options.value.floatPlaceholder">
<mat-select required>
<mat-option>-- None --</mat-option>
<mat-option value="option">Option</mat-option>
</mat-select>
<mat-placeholder><mat-icon>favorite</mat-icon> <b> Fancy</b> <i> placeholder</i></mat-placeholder>
</mat-form-field>
</div>
@@ -0,0 +1,19 @@
import {Component} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
/** @title Form field with placeholder */
@Component({
selector: 'form-field-placeholder-example',
templateUrl: 'form-field-placeholder-example.html',
styleUrls: ['form-field-placeholder-example.css']
})
export class FormFieldPlaceholderExample {
options: FormGroup;
constructor(fb: FormBuilder) {
this.options = fb.group({
hideRequired: false,
floatPlaceholder: 'auto',
});
}
}
@@ -0,0 +1,17 @@
.example-container {
display: flex;
flex-direction: column;
}
.example-container > * {
width: 100%;
}
.example-right-align {
text-align: right;
}
input.example-right-align::-webkit-outer-spin-button,
input.example-right-align::-webkit-inner-spin-button {
display: none;
}
@@ -0,0 +1,12 @@
<div class="example-container">
<mat-form-field>
<input matInput placeholder="Enter your password" [type]="hide ? 'password' : 'text'">
<mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' : 'visibility_off'}}</mat-icon>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Amount" type="number" class="example-right-align">
<span matPrefix>$&nbsp;</span>
<span matSuffix>.00</span>
</mat-form-field>
</div>
@@ -0,0 +1,11 @@
import {Component} from '@angular/core';
/** @title Form field with prefix & suffix */
@Component({
selector: 'form-field-prefix-suffix-example',
templateUrl: 'form-field-prefix-suffix-example.html',
styleUrls: ['form-field-prefix-suffix-example.css']
})
export class FormFieldPrefixSuffixExample {
hide = true;
}
@@ -0,0 +1,8 @@
.example-container {
display: flex;
flex-direction: column;
}
.example-container > * {
width: 100%;
}
@@ -0,0 +1,14 @@
<form class="example-container" [formGroup]="options" [style.fontSize.px]="getFontSize()">
<mat-form-field [color]="options.value.color">
<mat-select placeholder="Color" formControlName="color">
<mat-option value="primary">Primary</mat-option>
<mat-option value="accent">Accent</mat-option>
<mat-option value="warn">Warn</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field [color]="options.value.color">
<input matInput type="number" placeholder="Font size (px)" formControlName="fontSize" min="10">
<mat-error *ngIf="options.get('fontSize').invalid">Min size: 10px</mat-error>
</mat-form-field>
</form>
@@ -0,0 +1,23 @@
import {Component} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
/** @title Form field theming */
@Component({
selector: 'form-field-theming-example',
templateUrl: 'form-field-theming-example.html',
styleUrls: ['form-field-theming-example.css']
})
export class FormFieldThemingExample {
options: FormGroup;
constructor(fb: FormBuilder) {
this.options = fb.group({
'color': 'primary',
'fontSize': [16, Validators.min(10)],
});
}
getFontSize() {
return Math.max(10, this.options.value.fontSize);
}
}