From b35a2642ec4a25b62270083c25e95c6bd1819fad Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 22 Jul 2014 12:03:14 +0200 Subject: [PATCH] feat(form): Add support for input[type=color] Closes #611 Closes #1080 --- example/web/hello_world.dart | 1 + example/web/hello_world.html | 5 +- lib/directive/module.dart | 1 + lib/directive/ng_model.dart | 5 +- lib/directive/ng_model_validators.dart | 16 ++++++ test/angular_spec.dart | 1 + test/directive/ng_model_spec.dart | 54 ++++++++++++++++++++ test/directive/ng_model_validators_spec.dart | 27 +++++++++- 8 files changed, 105 insertions(+), 5 deletions(-) diff --git a/example/web/hello_world.dart b/example/web/hello_world.dart index ab8953f72..871164ace 100644 --- a/example/web/hello_world.dart +++ b/example/web/hello_world.dart @@ -6,6 +6,7 @@ import 'package:angular/application_factory.dart'; publishAs: 'ctrl') class HelloWorld { String name = "world"; + String color = "#aaaaaa"; } main() { diff --git a/example/web/hello_world.html b/example/web/hello_world.html index 705607d48..addcb05b2 100644 --- a/example/web/hello_world.html +++ b/example/web/hello_world.html @@ -5,8 +5,9 @@ -

Hello {{ctrl.name}}!

-name: +

Hello {{ctrl.name}}!

+Name: +Color: diff --git a/lib/directive/module.dart b/lib/directive/module.dart index 04c7a7b7c..29b040ced 100644 --- a/lib/directive/module.dart +++ b/lib/directive/module.dart @@ -121,5 +121,6 @@ class DirectiveModule extends Module { bind(NgModelPatternValidator, toValue: null); bind(NgModelMinLengthValidator, toValue: null); bind(NgModelMaxLengthValidator, toValue: null); + bind(NgModelColorValidator, toValue: null); } } diff --git a/lib/directive/ng_model.dart b/lib/directive/ng_model.dart index c09a47388..4199a33df 100644 --- a/lib/directive/ng_model.dart +++ b/lib/directive/ng_model.dart @@ -332,7 +332,7 @@ class InputCheckbox { * * **Usage** * - * + * * * * When the `ng-model` attribute is present on the input element, @@ -348,6 +348,7 @@ class InputCheckbox { @Decorator(selector: 'input[type=email][ng-model]') @Decorator(selector: 'input[type=search][ng-model]') @Decorator(selector: 'input[type=tel][ng-model]') +@Decorator(selector: 'input[type=color][ng-model]') class InputTextLike { final dom.Element inputElement; final NgModel ngModel; @@ -707,7 +708,7 @@ class NgValue { @NgOneWay('ng-value') void set value(val) { - this._value = val; + _value = val; } dynamic get value => _value == null ? (element as dynamic).value : _value; } diff --git a/lib/directive/ng_model_validators.dart b/lib/directive/ng_model_validators.dart index b0537399f..ed051ab3f 100644 --- a/lib/directive/ng_model_validators.dart +++ b/lib/directive/ng_model_validators.dart @@ -68,6 +68,22 @@ class NgModelUrlValidator implements NgValidator { modelValue == null || modelValue.isEmpty || URL_REGEXP.hasMatch(modelValue); } +/** + * Validates the model to see if its contents match a valid color pattern. + */ +@Decorator(selector: 'input[type=color][ng-model]') +class NgModelColorValidator implements NgValidator { + static final COLOR_REGEXP = new RegExp(r'^#[0-9a-f]{6}$', caseSensitive: false); + final String name = 'ng-color'; + + NgModelColorValidator(NgModel ngModel) { + ngModel.addValidator(this); + } + + bool isValid(modelValue) => + modelValue == null || modelValue.isEmpty || COLOR_REGEXP.hasMatch(modelValue); +} + /** * Validates the model to see if its contents match a valid email pattern. */ diff --git a/test/angular_spec.dart b/test/angular_spec.dart index 96d01e85a..091ff6e00 100644 --- a/test/angular_spec.dart +++ b/test/angular_spec.dart @@ -189,6 +189,7 @@ main() { "angular.directive.NgInclude", "angular.directive.NgModel", "angular.directive.NgModelOptions", + "angular.directive.NgModelColorValidator", "angular.directive.NgModelConverter", "angular.directive.NgModelEmailValidator", "angular.directive.NgModelMaxLengthValidator", diff --git a/test/directive/ng_model_spec.dart b/test/directive/ng_model_spec.dart index b952f9408..e7418dd61 100644 --- a/test/directive/ng_model_spec.dart +++ b/test/directive/ng_model_spec.dart @@ -1105,6 +1105,60 @@ void main() { }); }); + describe('type="color"', () { + it('should update input value from model', () { + _.compile(''); + _.rootScope.apply(); + + expect((_.rootElement as dom.InputElement).value).toEqual('#000000'); + + _.rootScope.apply('model = "#123456"'); + expect((_.rootElement as dom.InputElement).value).toEqual('#123456'); + }); + + it('should render as #000000 on default and when a null value is present', () { + _.compile(''); + _.rootScope.apply(); + + expect((_.rootElement as dom.InputElement).value).toEqual('#000000'); + + _.rootScope.apply('model = null'); + expect((_.rootElement as dom.InputElement).value).toEqual('#000000'); + }); + + it('should update model from the input value', () { + _.compile(''); + Probe probe = _.rootScope.context['p']; + var ngModel = probe.directive(NgModel); + InputElement inputElement = probe.element; + + inputElement.value = '#000000'; + _.triggerEvent(inputElement, 'change'); + expect(_.rootScope.context['model']).toEqual('#000000'); + + inputElement.value = '#ffffff'; + var input = probe.directive(InputTextLike); + input.processValue(); + expect(_.rootScope.context['model']).toEqual('#ffffff'); + }); + + it('should only render the input value upon the next digest', (Scope scope) { + _.compile(''); + Probe probe = _.rootScope.context['p']; + var ngModel = probe.directive(NgModel); + InputElement inputElement = probe.element; + + ngModel.render('#aabbcc'); + scope.context['model'] = '#aabbcc'; + + expect(inputElement.value).not.toEqual('#aabbcc'); + + scope.apply(); + + expect(inputElement.value).toEqual('#aabbcc'); + }); + }); + describe('contenteditable', () { it('should update content from model', () { _.compile('

'); diff --git a/test/directive/ng_model_validators_spec.dart b/test/directive/ng_model_validators_spec.dart index 3aa9d54ee..93fbeede6 100644 --- a/test/directive/ng_model_validators_spec.dart +++ b/test/directive/ng_model_validators_spec.dart @@ -111,6 +111,31 @@ void main() { }); }); + describe('[type="color"]', () { + it('should validate the input field given a valid or invalid color', (RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = 'red'; + }); + + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = '#ff0000'; + }); + + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + }); + }); + describe('[type="email"]', () { it('should validate the input field given a valid or invalid email address', (RootScope scope) { _.compile(''); @@ -627,6 +652,6 @@ void main() { expect(model.valid).toBe(true); }); - }); + }); }); }