From cdff6ba5aff82f7f8a798debcad0c7d4d36f9883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Thu, 27 Feb 2014 16:36:40 -0500 Subject: [PATCH 1/3] fix(NgForm): use map notation for controls and dot notation for instance properties --- lib/directive/ng_form.dart | 50 ++------------------------------ test/directive/ng_form_spec.dart | 18 ++++++++++++ 2 files changed, 20 insertions(+), 48 deletions(-) diff --git a/lib/directive/ng_form.dart b/lib/directive/ng_form.dart index 453f5f056..55936b81d 100644 --- a/lib/directive/ng_form.dart +++ b/lib/directive/ng_form.dart @@ -21,7 +21,7 @@ part of angular.directive; selector: '[ng-form]', publishTypes : const [NgControl], visibility: NgDirective.CHILDREN_VISIBILITY) -class NgForm extends NgControl implements Map { +class NgForm extends NgControl { /** * Instantiates a new instance of NgForm. Upon creation, the instance of the * class will be bound to the formName property on the scope (where formName @@ -54,56 +54,10 @@ class NgForm extends NgControl implements Map { _scope.context[name] = this; } - //FIXME: fix this reflection bug that shows up when Map is implemented - operator []=(String key, value) { - if (key == 'name') { - name = value; - } else { - _controlByName[key] = value; - } - } - - //FIXME: fix this reflection bug that shows up when Map is implemented - operator[](name) { - if (name == 'valid') { - return valid; - } else if (name == 'invalid') { - return invalid; - } else { - return _controlByName[name]; - } - } - - bool get isEmpty => false; - bool get isNotEmpty => !isEmpty; - get values => null; - get keys => null; - get length => null; - clear() => null; - remove(_) => null; - containsKey(_) => false; - containsValue(_) => false; - addAll(_) => null; - forEach(_) => null; - putIfAbsent(_, __) => null; + NgControl operator[](name) => _controlByName[name]; } class NgNullForm extends NgNullControl implements NgForm { NgNullForm() {} - operator[](name) {} - operator []=(String name, value) {} - - bool get isEmpty => false; - bool get isNotEmpty => true; - get values => null; - get keys => null; - get length => null; - clear() => null; - remove(_) => null; - containsKey(_) => false; - containsValue(_) => false; - addAll(_) => null; - forEach(_) => null; - putIfAbsent(_, __) => null; } diff --git a/test/directive/ng_form_spec.dart b/test/directive/ng_form_spec.dart index df8187c30..1987a4c0f 100644 --- a/test/directive/ng_form_spec.dart +++ b/test/directive/ng_form_spec.dart @@ -462,6 +462,24 @@ void main() { })); }); + it("should use map notation to fetch controls", inject((TestBed _) { + Scope s = _.rootScope; + s.context['name'] = 'cool'; + + var form = _.compile('
' + + ' ' + + '
'); + + NgForm formModel = s.context['myForm']; + Probe probe = s.context['i']; + var model = probe.directive(NgModel); + + expect(s.eval('name')).toEqual('cool'); + expect(s.eval('myForm.name')).toEqual('myForm'); + expect(s.eval('myForm["name"]')).toBe(model); + expect(s.eval('myForm["name"].name')).toEqual("name"); + })); + describe('regression tests: form', () { it('should be resolvable by injector if configured by user.', () { module((Module module) { From ccce19cc2ef71b6fb71b66fec678373f734aa304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Tue, 4 Mar 2014 16:55:44 -0500 Subject: [PATCH 2/3] fix(NgForm): always return the first matching control when using map notation on a NgForm instance --- lib/directive/ng_control.dart | 12 ++++++++---- lib/directive/ng_form.dart | 6 +++++- test/directive/ng_form_spec.dart | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/directive/ng_control.dart b/lib/directive/ng_control.dart index b2b9b1f59..31bbc83c9 100644 --- a/lib/directive/ng_control.dart +++ b/lib/directive/ng_control.dart @@ -26,7 +26,7 @@ abstract class NgControl implements NgDetachAware { final Map> errors = new Map>(); final List _controls = new List(); - final Map _controlByName = new Map(); + final Map> _controlByName = new Map>(); NgControl(Scope this._scope, dom.Element this._element, Injector injector, NgAnimate this._animate) @@ -144,7 +144,7 @@ abstract class NgControl implements NgDetachAware { addControl(NgControl control) { _controls.add(control); if (control.name != null) { - _controlByName[control.name] = control; + _controlByName.putIfAbsent(control.name, () => new List()).add(control); } } @@ -157,8 +157,12 @@ abstract class NgControl implements NgDetachAware { */ removeControl(NgControl control) { _controls.remove(control); - if (control.name != null) { - _controlByName.remove(control.name); + String key = control.name; + if (key != null && _controlByName.containsKey(key)) { + _controlByName[key].remove(control); + if (_controlByName[key].isEmpty) { + _controlByName.remove(key); + } } } diff --git a/lib/directive/ng_form.dart b/lib/directive/ng_form.dart index 55936b81d..c2c0cdbb4 100644 --- a/lib/directive/ng_form.dart +++ b/lib/directive/ng_form.dart @@ -54,7 +54,11 @@ class NgForm extends NgControl { _scope.context[name] = this; } - NgControl operator[](name) => _controlByName[name]; + NgControl operator[](name) { + if (_controlByName.containsKey(name)) { + return _controlByName[name][0]; + } + } } class NgNullForm extends NgNullControl implements NgForm { diff --git a/test/directive/ng_form_spec.dart b/test/directive/ng_form_spec.dart index 1987a4c0f..05fd17ba3 100644 --- a/test/directive/ng_form_spec.dart +++ b/test/directive/ng_form_spec.dart @@ -20,6 +20,26 @@ void main() { expect(form.name).toEqual('myForm'); })); + it('should return the first control with the given name when accessed using map notation', + inject((Scope scope, TestBed _) { + + var element = $('
' + + ' ' + + ' ' + + '
'); + + _.compile(element); + scope.apply(); + + NgForm form = _.rootScope.context['myForm']; + NgModel one = _.rootScope.context['a'].directive(NgModel); + NgModel two = _.rootScope.context['b'].directive(NgModel); + + expect(one).not.toBe(two); + expect(form['model']).toBe(one); + expect(scope.eval("myForm['model']")).toBe(one); + })); + describe('pristine / dirty', () { it('should be set to pristine by default', inject((Scope scope, TestBed _) { var element = $('
'); From ab619d1e3a7b83168092303a16c794690ee8ebbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Tue, 4 Mar 2014 17:11:12 -0500 Subject: [PATCH 3/3] feat(NgForm): provide access to non-uniquely named control instances via form.controls --- lib/directive/ng_form.dart | 8 ++++++-- test/directive/ng_form_spec.dart | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/directive/ng_form.dart b/lib/directive/ng_form.dart index c2c0cdbb4..786365aaa 100644 --- a/lib/directive/ng_form.dart +++ b/lib/directive/ng_form.dart @@ -54,9 +54,11 @@ class NgForm extends NgControl { _scope.context[name] = this; } + get controls => _controlByName; + NgControl operator[](name) { - if (_controlByName.containsKey(name)) { - return _controlByName[name][0]; + if (controls.containsKey(name)) { + return controls[name][0]; } } } @@ -64,4 +66,6 @@ class NgForm extends NgControl { class NgNullForm extends NgNullControl implements NgForm { NgNullForm() {} operator[](name) {} + + get controls => null; } diff --git a/test/directive/ng_form_spec.dart b/test/directive/ng_form_spec.dart index 05fd17ba3..a23a32e8d 100644 --- a/test/directive/ng_form_spec.dart +++ b/test/directive/ng_form_spec.dart @@ -40,6 +40,30 @@ void main() { expect(scope.eval("myForm['model']")).toBe(one); })); + it('should return the all the controls with the given name', inject((Scope scope, TestBed _) { + var element = $('
' + + ' ' + + ' ' + + '
'); + + _.compile(element); + scope.apply(); + + NgForm form = _.rootScope.context['myForm']; + NgModel one = _.rootScope.context['a'].directive(NgModel); + NgModel two = _.rootScope.context['b'].directive(NgModel); + + expect(one).not.toBe(two); + + var controls = form.controls['model']; + expect(controls[0]).toBe(one); + expect(controls[1]).toBe(two); + + expect(scope.eval("myForm.controls['model'][0]")).toBe(one); + expect(scope.eval("myForm.controls['model'][1]")).toBe(two); + })); + + describe('pristine / dirty', () { it('should be set to pristine by default', inject((Scope scope, TestBed _) { var element = $('
');