From 14e36f414c34d60779974aafa3dd68ecbfe56bc0 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Sun, 10 Jan 2016 10:30:28 -0800 Subject: [PATCH] feat(form): use md-message/md-messages for form validation errors - support declaratively specifying validators in input basic usage example. - add md-messages directive for specifying a group to hold form messages. - add md-message to decorate elements inside of md-messages container to be associated with a particular error. - refactor messages and validation out of input/ path and into form/ path, which seems more appropriate. --- examples/components/input/basic_usage.html | 11 +-- ng2-material/all.ts | 12 +-- ng2-material/components/form/messages.ts | 83 +++++++++++++++++++ .../validators.ts} | 10 +++ ng2-material/components/input/input.ts | 23 ++--- 5 files changed, 111 insertions(+), 28 deletions(-) create mode 100644 ng2-material/components/form/messages.ts rename ng2-material/components/{input/input_validators.ts => form/validators.ts} (84%) diff --git a/examples/components/input/basic_usage.html b/examples/components/input/basic_usage.html index a482ec93..04bb30c0 100644 --- a/examples/components/input/basic_usage.html +++ b/examples/components/input/basic_usage.html @@ -61,16 +61,13 @@ - -
-
You must supply a postal code.
-
That doesn't look like a valid postal code.
-
- Don't use the long version silly...we don't need to be that specific... -
+
+
You must supply a postal code.
+
That doesn't look like a valid postal code.
+
Don't use the long version silly...we don't need to be that specific...
diff --git a/ng2-material/all.ts b/ng2-material/all.ts index d5c63738..7bc9e854 100644 --- a/ng2-material/all.ts +++ b/ng2-material/all.ts @@ -21,11 +21,12 @@ export * from './components/grid_list/grid_list'; import {MdIcon} from './components/icon/icon'; export * from './components/icon/icon'; -import { - MdInput, MdInputContainer, - MdPatternValidator, MdMaxLengthValidator, - INPUT_VALIDATORS -} from './components/input/input'; +import {MdPatternValidator, MdMaxLengthValidator,INPUT_VALIDATORS} from './components/form/validators'; +export * from './components/form/validators'; +import {MdMessage, MdMessages} from './components/form/messages'; +export * from './components/form/messages'; + +import {MdInput, MdInputContainer} from './components/input/input'; export * from './components/input/input'; import {MdList, MdListItem} from './components/list/list'; @@ -70,6 +71,7 @@ export const MATERIAL_DIRECTIVES: Type[] = CONST_EXPR([ MdGridList, MdGridTile, MdIcon, MdInput, MdInputContainer, MdPatternValidator, MdMaxLengthValidator, + MdMessage, MdMessages, MdList, MdListItem, MdPeekaboo, MdProgressLinear, diff --git a/ng2-material/components/form/messages.ts b/ng2-material/components/form/messages.ts new file mode 100644 index 00000000..b53e27b8 --- /dev/null +++ b/ng2-material/components/form/messages.ts @@ -0,0 +1,83 @@ +import {CONST_EXPR} from "angular2/src/facade/lang"; +import {NG_VALIDATORS} from "angular2/common"; +import { + Attribute, Input, Provider, Directive, Optional, SkipSelf, Host, OnDestroy, OnInit, + ContentChildren, QueryList, Query, AfterContentInit +} from "angular2/core"; +import {Validator, NgFormModel, NgControlName} from "angular2/common"; +import {Control} from "angular2/common"; +import {isPresent} from "angular2/src/facade/lang"; + + +@Directive({ + selector: '[md-message]', + host: { + '[hidden]': 'okay' + } +}) +export class MdMessage { + @Input('md-message') errorKey: string; + + okay: boolean = true; + + constructor(@Attribute('md-message') pattern: any) { + if (isPresent(pattern)) { + this.errorKey = pattern; + } + } +} + + +@Directive({ + selector: '[md-messages]', + host: { + 'md-messages': '', + '[hidden]': 'valid || !property?.touched', + '[class.md-valid]': 'valid && property?.touched', + '[class.md-invalid]': '!valid && property?.touched' + } +}) +export class MdMessages implements OnInit, OnDestroy { + + @Input('md-messages') property: string|NgControlName; + + valid: boolean; + + constructor(@Query(MdMessage) public messages: QueryList, + @Optional() @SkipSelf() @Host() public form: NgFormModel, + @Attribute('md-messages') pattern: any) { + if (isPresent(pattern)) { + this.property = pattern; + } + } + + private _unsubscribe: any = null; + + ngOnInit() { + if (this.property instanceof NgControlName) { + let ctrl: NgControlName = this.property; + this.form = ctrl.formDirective; + this._unsubscribe = (ctrl).update.subscribe(this._valueChanged.bind(this)); + } + else { + if (!this.form) { + throw new Error('md-messages cannot bind to text property without a parent NgFormModel'); + } + console.log('init formbuilder property: ' + this.property); + } + } + + ngOnDestroy(): any { + this._unsubscribe(); + } + + private _valueChanged(newValue: string) { + let ctrl: NgControlName = this.property; + this.valid = !ctrl.errors; + if (ctrl.errors) { + this.messages.toArray().forEach((m: MdMessage) => { + m.okay = !isPresent(ctrl.errors[m.errorKey]); + }); + } + } +} diff --git a/ng2-material/components/input/input_validators.ts b/ng2-material/components/form/validators.ts similarity index 84% rename from ng2-material/components/input/input_validators.ts rename to ng2-material/components/form/validators.ts index 9f87eae0..0deab7f1 100644 --- a/ng2-material/components/input/input_validators.ts +++ b/ng2-material/components/form/validators.ts @@ -16,6 +16,16 @@ const PATTERN_VALIDATOR = CONST_EXPR(new Provider(NG_VALIDATORS, { }) export class MdPatternValidator implements Validator { + /** + * Static method that returns a validator function for use + * with {@see FormBuilder}. + * @param pattern The regular expression to match. + * @returns A Validator function that may be used with + */ + static match(pattern:string):Function { + return new MdPatternValidator(pattern).validate; + } + @Input('mdPattern') mdPattern: string; constructor(@Attribute('mdPattern') pattern: any) { diff --git a/ng2-material/components/input/input.ts b/ng2-material/components/input/input.ts index a1b4a5a7..4b0d45ad 100644 --- a/ng2-material/components/input/input.ts +++ b/ng2-material/components/input/input.ts @@ -1,6 +1,6 @@ import { Directive, Attribute, Host, SkipSelf, AfterContentInit, ElementRef, forwardRef, OnChanges, ContentChild, - Query, QueryList, OnInit, Optional + Query, QueryList, Optional } from 'angular2/core'; import {NgControlName, FORM_PROVIDERS} from 'angular2/common'; @@ -11,9 +11,6 @@ import {isPresent} from 'angular2/src/facade/lang'; import {DOM} from "angular2/src/platform/dom/dom_adapter"; import {TimerWrapper} from "angular2/src/facade/async"; -export * from './input_validators'; - - // TODO(jd):