Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 89f06bb

Browse files
committed
refactor(forms): remove _NgModelValidator in favor of using a shared interface
_NgModelValidator acted as the base class for validators in AngularDart with earlier releases. This works, but it poses a problem for custom validators that exist outside of the AngularDart core. This is because Inheritance is a problem since it is highly limited when it comes to further refactoring and customization. Interfaces are more versatile. Therefore all core and custom validators must implement the shared interface so that they can be attached to ngModel for validation purposes.
1 parent ce6e58f commit 89f06bb

File tree

3 files changed

+71
-136
lines changed

3 files changed

+71
-136
lines changed

lib/directive/ng_model.dart

+6-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class NgModel extends NgControl {
1616
BoundSetter setter = (_, [__]) => null;
1717

1818
String _exp;
19-
final List<_NgModelValidator> _validators = new List<_NgModelValidator>();
19+
final _validators = <NgValidatable>[];
2020

2121
bool _watchCollection;
2222
Function _removeWatch = () => null;
@@ -69,29 +69,29 @@ class NgModel extends NgControl {
6969
validate() {
7070
if (validators.isNotEmpty) {
7171
validators.forEach((validator) {
72-
setValidity(validator.name, validator.isValid());
72+
setValidity(validator.name, validator.isValid(viewValue));
7373
});
7474
} else {
7575
valid = true;
7676
}
7777
}
7878

79-
setValidity(String name, bool isValid) {
80-
this.updateControlValidity(this, name, isValid);
79+
setValidity(String name, bool valid) {
80+
this.updateControlValidity(this, name, valid);
8181
}
8282

8383
/**
8484
* Registers a validator into the model to consider when running validate().
8585
*/
86-
addValidator(_NgModelValidator v) {
86+
addValidator(NgValidatable v) {
8787
validators.add(v);
8888
validate();
8989
}
9090

9191
/**
9292
* De-registers a validator from the model.
9393
*/
94-
removeValidator(_NgModelValidator v) {
94+
removeValidator(NgValidatable v) {
9595
validators.remove(v);
9696
validate();
9797
}
+62-129
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,30 @@
11
part of angular.directive;
22

3-
/**
4-
* _NgModelValidator refers to the required super-class which is used when creating
5-
* validation services that are used with [ngModel]. It is expected that any child-classes
6-
* that inherit from this perform the necessary logic to return a simple true/false response
7-
* when validating the contents of the model data.
8-
*/
9-
abstract class _NgModelValidator {
10-
final dom.Element inputElement;
11-
final NgModel ngModel;
12-
final Scope scope;
13-
bool _listening = false;
14-
15-
_NgModelValidator(this.inputElement, this.ngModel, this.scope);
16-
17-
/**
18-
* Registers the validator with to attached model.
19-
*/
20-
void listen() {
21-
if (!_listening) {
22-
_listening = true;
23-
this.ngModel.addValidator(this);
24-
}
25-
}
26-
27-
get value => ngModel.viewValue;
28-
29-
/**
30-
* De-registers the validator with to attached model.
31-
*/
32-
void unlisten() {
33-
if (_listening) {
34-
_listening = false;
35-
this.ngModel.removeValidator(this);
36-
}
37-
}
38-
39-
/**
40-
* Returns true/false depending on the status of the validator's validation mechanism
41-
*/
42-
bool isValid();
3+
abstract class NgValidatable {
4+
String get name;
5+
bool isValid(value);
436
}
447

458
/**
469
* Validates the model depending if required or ng-required is present on the element.
4710
*/
48-
@NgDirective(selector: '[ng-model][required]')
11+
@NgDirective(
12+
selector: '[ng-model][required]')
4913
@NgDirective(
5014
selector: '[ng-model][ng-required]',
5115
map: const {'ng-required': '=>required'})
52-
class NgModelRequiredValidator extends _NgModelValidator {
53-
bool _required;
54-
get name => 'required';
55-
56-
NgModelRequiredValidator(dom.Element inputElement, NgModel ngModel,
57-
Scope scope, NodeAttrs attrs):
58-
super(inputElement, ngModel, scope) {
59-
if (attrs['required'] != null) required = true;
60-
}
16+
class NgModelRequiredValidator implements NgValidatable {
17+
bool _required = true;
18+
19+
String get name => 'required';
20+
21+
NgModelRequiredValidator(NgModel ngModel) {
22+
ngModel.addValidator(this);
23+
}
6124

62-
bool isValid() {
25+
bool isValid(value) {
6326
// Any element which isn't required is always valid.
64-
if (!required) return true;
27+
if (!_required) return true;
6528
// Null is not a value, therefore not valid.
6629
if (value == null) return false;
6730
// Empty lists and/or strings are not valid.
@@ -70,70 +33,60 @@ class NgModelRequiredValidator extends _NgModelValidator {
7033
return !((value is List || value is String) && value.isEmpty);
7134
}
7235

73-
@NgAttr('required')
74-
get required => _required;
7536
set required(value) {
76-
if (value is String) return;
77-
if ((_required = value) == true) {
78-
listen();
79-
} else {
80-
unlisten();
81-
}
37+
_required = value == null ? false : value;
8238
}
8339
}
8440

8541
/**
8642
* Validates the model to see if its contents match a valid URL pattern.
8743
*/
8844
@NgDirective(selector: 'input[type=url][ng-model]')
89-
class NgModelUrlValidator extends _NgModelValidator {
45+
class NgModelUrlValidator implements NgValidatable {
9046
static final URL_REGEXP = new RegExp(
9147
r'^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?' +
9248
r'(\/|\/([\w#!:.?+=&%@!\-\/]))?$');
9349

94-
get name => 'url';
50+
String get name => 'url';
9551

96-
NgModelUrlValidator(dom.Element inputElement, NgModel ngModel, Scope scope):
97-
super(inputElement, ngModel, scope) {
98-
listen();
99-
}
52+
NgModelUrlValidator(NgModel ngModel) {
53+
ngModel.addValidator(this);
54+
}
10055

101-
bool isValid() =>
56+
bool isValid(value) =>
10257
value == null || value.isEmpty || URL_REGEXP.hasMatch(value);
10358
}
10459

10560
/**
10661
* Validates the model to see if its contents match a valid email pattern.
10762
*/
10863
@NgDirective(selector: 'input[type=email][ng-model]')
109-
class NgModelEmailValidator extends _NgModelValidator {
64+
class NgModelEmailValidator implements NgValidatable {
11065
static final EMAIL_REGEXP = new RegExp(
11166
r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$');
11267

113-
get name => 'email';
68+
String get name => 'email';
11469

115-
NgModelEmailValidator(dom.Element inputElement, NgModel ngModel, Scope scope):
116-
super(inputElement, ngModel, scope) {
117-
listen();
118-
}
70+
NgModelEmailValidator(NgModel ngModel) {
71+
ngModel.addValidator(this);
72+
}
11973

120-
bool isValid() =>
74+
bool isValid(value) =>
12175
value == null || value.isEmpty || EMAIL_REGEXP.hasMatch(value);
12276
}
12377

12478
/**
12579
* Validates the model to see if its contents match a valid number.
12680
*/
12781
@NgDirective(selector: 'input[type=number][ng-model]')
128-
class NgModelNumberValidator extends _NgModelValidator {
129-
get name => 'number';
82+
class NgModelNumberValidator implements NgValidatable {
83+
String get name => 'number';
13084

131-
NgModelNumberValidator(dom.Element inputElement, NgModel ngModel, Scope scope):
132-
super(inputElement, ngModel, scope) {
133-
listen();
134-
}
85+
NgModelNumberValidator(NgModel ngModel) {
86+
ngModel.addValidator(this);
87+
}
13588

136-
bool isValid() {
89+
bool isValid(value) {
13790
if (value != null) {
13891
try {
13992
num val = double.parse(value.toString());
@@ -153,36 +106,24 @@ class NgModelNumberValidator extends _NgModelValidator {
153106
@NgDirective(
154107
selector: '[ng-model][ng-pattern]',
155108
map: const {'ng-pattern': '=>pattern'})
156-
class NgModelPatternValidator extends _NgModelValidator {
109+
class NgModelPatternValidator implements NgValidatable {
157110
RegExp _pattern;
158111

159-
get name => 'pattern';
160-
161-
NgModelPatternValidator(dom.Element inputElement, NgModel ngModel, Scope scope):
162-
super(inputElement, ngModel, scope) {
163-
listen();
164-
}
112+
String get name => 'pattern';
165113

166-
bool isValid() {
167-
if (_pattern != null && value != null && value.length > 0) {
168-
return _pattern.hasMatch(ngModel.viewValue);
169-
}
114+
NgModelPatternValidator(NgModel ngModel) {
115+
ngModel.addValidator(this);
116+
}
170117

118+
bool isValid(value) {
171119
//remember, only required validates for the input being empty
172-
return true;
120+
return _pattern == null || value == null || value.length == 0 ||
121+
_pattern.hasMatch(value);
173122
}
174123

175124
@NgAttr('pattern')
176-
get pattern => _pattern;
177-
set pattern(val) {
178-
if (val != null && val.length > 0) {
179-
_pattern = new RegExp(val);
180-
listen();
181-
} else {
182-
_pattern = null;
183-
unlisten();
184-
}
185-
}
125+
set pattern(val) =>
126+
_pattern = val != null && val.length > 0 ? new RegExp(val) : null;
186127
}
187128

188129
/**
@@ -194,29 +135,24 @@ class NgModelPatternValidator extends _NgModelValidator {
194135
@NgDirective(
195136
selector: '[ng-model][ng-minlength]',
196137
map: const {'ng-minlength': '=>minlength'})
197-
class NgModelMinLengthValidator extends _NgModelValidator {
138+
class NgModelMinLengthValidator implements NgValidatable {
198139
int _minlength;
199140

200-
get name => 'minlength';
141+
String get name => 'minlength';
201142

202-
NgModelMinLengthValidator(dom.Element inputElement, NgModel ngModel,
203-
Scope scope) : super(inputElement, ngModel, scope) {
204-
listen();
205-
}
143+
NgModelMinLengthValidator(NgModel ngModel) {
144+
ngModel.addValidator(this);
145+
}
206146

207-
bool isValid() {
147+
bool isValid(value) {
208148
//remember, only required validates for the input being empty
209-
if (_minlength == 0 || value == null || value.length == 0) {
210-
return true;
211-
}
212-
return value.length >= _minlength;
149+
return _minlength == 0 || value == null || value.length == 0 ||
150+
value.length >= _minlength;
213151
}
214152

215153
@NgAttr('minlength')
216-
get minlength => _minlength;
217-
set minlength(value) {
218-
_minlength = value == null ? 0 : int.parse(value.toString());
219-
}
154+
set minlength(value) =>
155+
_minlength = value == null ? 0 : int.parse(value.toString());
220156
}
221157

222158
/**
@@ -228,22 +164,19 @@ class NgModelMinLengthValidator extends _NgModelValidator {
228164
@NgDirective(
229165
selector: '[ng-model][ng-maxlength]',
230166
map: const {'ng-maxlength': '=>maxlength'})
231-
class NgModelMaxLengthValidator extends _NgModelValidator {
167+
class NgModelMaxLengthValidator implements NgValidatable {
232168
int _maxlength = 0;
233169

234-
get name => 'maxlength';
170+
String get name => 'maxlength';
235171

236-
NgModelMaxLengthValidator(dom.Element inputElement, NgModel ngModel,
237-
Scope scope): super(inputElement, ngModel, scope) {
238-
listen();
239-
}
172+
NgModelMaxLengthValidator(NgModel ngModel) {
173+
ngModel.addValidator(this);
174+
}
240175

241-
bool isValid() =>
176+
bool isValid(value) =>
242177
_maxlength == 0 || (value == null ? 0 : value.length) <= _maxlength;
243178

244179
@NgAttr('maxlength')
245-
get maxlength => _maxlength;
246-
set maxlength(value) {
247-
_maxlength = value == null ? 0 : int.parse(value.toString());
248-
}
180+
set maxlength(value) =>
181+
_maxlength = value == null ? 0 : int.parse(value.toString());
249182
}

test/directive/ng_model_validators_spec.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ describe('ngModel validators', () {
1010

1111
describe('required', () {
1212
it('should validate the input field if the required attribute is set', inject((Scope scope) {
13-
_.compile('<input type="text" ng-model="val" probe="i" required="true" />');
13+
_.compile('<input type="text" ng-model="val" probe="i" required />');
1414
Probe probe = _.rootScope.i;
1515
var model = probe.directive(NgModel);
1616

@@ -48,6 +48,8 @@ describe('ngModel validators', () {
4848
Probe probe = _.rootScope.i;
4949
var model = probe.directive(NgModel);
5050

51+
_.rootScope.$apply();
52+
5153
model.validate();
5254
expect(model.valid).toEqual(true);
5355
expect(model.invalid).toEqual(false);

0 commit comments

Comments
 (0)