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

feat(forms): provide support for touch and untouched control flags #591

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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