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

Commit

Permalink
feat(forms): provide support for touch and untouched control flags
Browse files Browse the repository at this point in the history
Closes #591
  • Loading branch information
matsko authored and mhevery committed Feb 25, 2014
1 parent 768a809 commit 634c62b
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 4 deletions.
34 changes: 33 additions & 1 deletion lib/directive/ng_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ abstract class NgControl implements NgDetachAware {
static const NG_INVALID_CLASS = "ng-invalid";
static const NG_PRISTINE_CLASS = "ng-pristine";
static const NG_DIRTY_CLASS = "ng-dirty";
static const NG_TOUCHED_CLASS = "ng-touched";
static const NG_UNTOUCHED_CLASS = "ng-untouched";
static const NG_SUBMIT_VALID_CLASS = "ng-submit-valid";
static const NG_SUBMIT_INVALID_CLASS = "ng-submit-invalid";

Expand All @@ -13,6 +15,8 @@ abstract class NgControl implements NgDetachAware {
bool _pristine;
bool _valid;
bool _invalid;
bool _touched;
bool _untouched;

final Scope _scope;
final NgControl _parentControl;
Expand All @@ -26,6 +30,8 @@ abstract class NgControl implements NgDetachAware {
: _parentControl = injector.parent.get(NgControl)
{
pristine = true;
untouched = true;

_scope.on('submitNgControl').listen((e) => _onSubmit(e.data));
}

Expand All @@ -37,6 +43,7 @@ abstract class NgControl implements NgDetachAware {

reset() {
_scope.broadcast('resetNgModel');
untouched = true;
}

_onSubmit(bool valid) {
Expand Down Expand Up @@ -91,6 +98,25 @@ abstract class NgControl implements NgDetachAware {
element.classes..remove(NG_VALID_CLASS)..add(NG_INVALID_CLASS);
}

get touched => _touched;
set touched(value) {
_touched = true;
_untouched = false;

element.classes..remove(NG_UNTOUCHED_CLASS)..add(NG_TOUCHED_CLASS);

//as soon as one of the controls/models is touched
//then all of the parent controls are touched as well
_parentControl.touched = true;
}

get untouched => _untouched;
set untouched(value) {
_touched = false;
_untouched = true;
element.classes..remove(NG_TOUCHED_CLASS)..add(NG_UNTOUCHED_CLASS);
}

/**
* Registers a form control into the form for validation.
*
Expand Down Expand Up @@ -158,7 +184,7 @@ abstract class NgControl implements NgDetachAware {
}

class NgNullControl implements NgControl {
var _name, _dirty, _valid, _invalid, _pristine, _element;
var _name, _dirty, _valid, _invalid, _pristine, _element, _touched, _untouched;
var _controls, _scope, _parentControl, _controlName;
var errors, _controlByName;
dom.Element element;
Expand All @@ -185,6 +211,12 @@ class NgNullControl implements NgControl {
get invalid => null;
set invalid(value) {}

get touched => null;
set touched(value) {}

get untouched => null;
set untouched(value) {}

reset() => null;
detach() => null;

Expand Down
1 change: 1 addition & 0 deletions lib/directive/ng_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class NgForm extends NgControl implements Map<String, NgControl> {
element.onSubmit.listen((event) {
event.preventDefault();
_scope.broadcast('submitNgControl', valid == null ? false : valid);
reset();
});
}
}
Expand Down
8 changes: 7 additions & 1 deletion lib/directive/ng_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class NgModel extends NgControl implements NgAttachAware {
}

reset() {
untouched = true;
modelValue = _lastValue;
}

Expand Down Expand Up @@ -203,7 +204,12 @@ class InputTextLikeDirective {
};
inputElement
..onChange.listen(processValue)
..onInput.listen(processValue);
..onInput.listen(processValue)
..onBlur.listen((e) {
if(ngModel.touched == null || ngModel.touched == false) {
ngModel.touched = true;
}
});
}

processValue([_]) {
Expand Down
40 changes: 40 additions & 0 deletions test/directive/ng_form_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,46 @@ describe('form', () {
expect(model.modelValue).toEqual('animal');
expect(model.viewValue).toEqual('animal');
}));

it('should set the form control to be untouched when the model is reset or submitted', inject((TestBed _) {
var form = _.compile('<form name="duperForm">' +
' <input type="text" ng-model="myModel" probe="i" />' +
'</form>');
var model = _.rootScope.context['i'].directive(NgModel);
var input = model.element;

NgForm formModel = _.rootScope.context['duperForm'];

expect(formModel.touched).toBe(false);
expect(formModel.untouched).toBe(true);
expect(form.classes.contains('ng-touched')).toBe(false);
expect(form.classes.contains('ng-untouched')).toBe(true);

_.triggerEvent(input, 'blur');

expect(formModel.touched).toBe(true);
expect(formModel.untouched).toBe(false);
expect(form.classes.contains('ng-touched')).toBe(true);
expect(form.classes.contains('ng-untouched')).toBe(false);

formModel.reset();

expect(formModel.touched).toBe(false);
expect(formModel.untouched).toBe(true);
expect(form.classes.contains('ng-touched')).toBe(false);
expect(form.classes.contains('ng-untouched')).toBe(true);

_.triggerEvent(input, 'blur');

expect(formModel.touched).toBe(true);

_.triggerEvent(form, 'submit');

expect(formModel.touched).toBe(false);
expect(formModel.untouched).toBe(true);
expect(form.classes.contains('ng-touched')).toBe(false);
expect(form.classes.contains('ng-untouched')).toBe(true);
}));
});

describe('regression tests: form', () {
Expand Down
22 changes: 20 additions & 2 deletions test/directive/ng_model_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -707,8 +707,8 @@ describe('ng-model', () {

var model = probe.directive(NgModel);

var input1 = element.query("#on");
var input2 = element.query("#off");
var input1 = element.querySelector("#on");
var input2 = element.querySelector("#off");

expect(model.pristine).toEqual(true);
expect(model.dirty).toEqual(false);
Expand Down Expand Up @@ -1021,6 +1021,24 @@ describe('ng-model', () {
expect(model.modelValue).toEqual('animal');
expect(model.viewValue).toEqual('animal');
});

it('should set the model to be untouched when the model is reset', () {
var input = _.compile('<input type="text" ng-model="myModel" probe="i" />');
var model = _.rootScope.context['i'].directive(NgModel);

expect(model.touched).toBe(false);
expect(model.untouched).toBe(true);

_.triggerEvent(input, 'blur');

expect(model.touched).toBe(true);
expect(model.untouched).toBe(false);

model.reset();

expect(model.touched).toBe(false);
expect(model.untouched).toBe(true);
});
});
});

Expand Down

0 comments on commit 634c62b

Please sign in to comment.