Skip to content

Commit ec3a782

Browse files
committed
feat(forms): added touched and untouched to Control
1 parent f303f0c commit ec3a782

8 files changed

Lines changed: 66 additions & 11 deletions

File tree

‎modules/angular2/src/forms/directives/checkbox_value_accessor.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@ import {ControlValueAccessor} from './control_value_accessor';
1717
@Directive({
1818
selector:
1919
'input[type=checkbox][ng-control],input[type=checkbox][ng-form-control],input[type=checkbox][ng-model]',
20-
hostListeners: {'change': 'onChange($event.target.checked)'},
20+
hostListeners: {'change': 'onChange($event.target.checked)', 'blur': 'onTouched()'},
2121
hostProperties: {'checked': 'checked'}
2222
})
2323
export class CheckboxControlValueAccessor implements ControlValueAccessor {
2424
checked: boolean;
2525
onChange: Function;
26+
onTouched: Function;
2627

2728
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
2829
this.onChange = (_) => {};
30+
this.onTouched = (_) => {};
2931
cd.valueAccessor = this;
3032
}
3133

@@ -34,5 +36,6 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor {
3436
this._elementRef.boundElementIndex, 'checked', value)
3537
}
3638

37-
registerOnChange(fn) { this.onChange = fn; }
39+
registerOnChange(fn): void { this.onChange = fn; }
40+
registerOnTouched(fn): void { this.onTouched = fn; }
3841
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export interface ControlValueAccessor {
22
writeValue(obj: any): void;
3-
registerOnChange(fun: any): void;
3+
registerOnChange(fn: any): void;
4+
registerOnTouched(fn: any): void;
45
}

‎modules/angular2/src/forms/directives/default_value_accessor.ts‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,21 @@ import {ControlValueAccessor} from './control_value_accessor';
1919
@Directive({
2020
selector:
2121
'input:not([type=checkbox])[ng-control],textarea[ng-control],input:not([type=checkbox])[ng-form-control],textarea[ng-form-control],input:not([type=checkbox])[ng-model],textarea[ng-model]',
22-
hostListeners:
23-
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
22+
hostListeners: {
23+
'change': 'onChange($event.target.value)',
24+
'input': 'onChange($event.target.value)',
25+
'blur': 'onTouched()'
26+
},
2427
hostProperties: {'value': 'value'}
2528
})
2629
export class DefaultValueAccessor implements ControlValueAccessor {
2730
value = null;
2831
onChange: Function;
32+
onTouched: Function;
2933

3034
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
3135
this.onChange = (_) => {};
36+
this.onTouched = (_) => {};
3237
cd.valueAccessor = this;
3338
}
3439

@@ -37,5 +42,6 @@ export class DefaultValueAccessor implements ControlValueAccessor {
3742
this._elementRef.boundElementIndex, 'value', value)
3843
}
3944

40-
registerOnChange(fn) { this.onChange = fn; }
41-
}
45+
registerOnChange(fn): void { this.onChange = fn; }
46+
registerOnTouched(fn): void { this.onTouched = fn; }
47+
}

‎modules/angular2/src/forms/directives/select_control_value_accessor.ts‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,21 @@ import {ControlValueAccessor} from './control_value_accessor';
1818
*/
1919
@Directive({
2020
selector: 'select[ng-control],select[ng-form-control],select[ng-model]',
21-
hostListeners:
22-
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
21+
hostListeners: {
22+
'change': 'onChange($event.target.value)',
23+
'input': 'onChange($event.target.value)',
24+
'blur': 'onTouched()'
25+
},
2326
hostProperties: {'value': 'value'}
2427
})
2528
export class SelectControlValueAccessor implements ControlValueAccessor {
2629
value = null;
2730
onChange: Function;
31+
onTouched: Function;
2832

2933
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
3034
this.onChange = (_) => {};
35+
this.onTouched = (_) => {};
3136
this.value = '';
3237
cd.valueAccessor = this;
3338
}
@@ -37,5 +42,6 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
3742
this._elementRef.boundElementIndex, 'value', value)
3843
}
3944

40-
registerOnChange(fn) { this.onChange = fn; }
41-
}
45+
registerOnChange(fn): void { this.onChange = fn; }
46+
registerOnTouched(fn): void { this.onTouched = fn; }
47+
}

‎modules/angular2/src/forms/directives/shared.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export function setUpControl(c: Control, dir: ControlDirective) {
2727

2828
// model -> view
2929
c.registerOnChange(newValue => dir.valueAccessor.writeValue(newValue));
30+
31+
// touched
32+
dir.valueAccessor.registerOnTouched(() => c.touch());
3033
}
3134

3235
function _throwError(dir: ControlDirective, message: string): void {

‎modules/angular2/src/forms/model.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class AbstractControl {
3030
_status: string;
3131
_errors: StringMap<string, any>;
3232
_pristine: boolean;
33+
_touched: boolean;
3334
_parent: any; /* ControlGroup | ControlArray */
3435
validator: Function;
3536

@@ -38,6 +39,7 @@ export class AbstractControl {
3839
constructor(validator: Function) {
3940
this.validator = validator;
4041
this._pristine = true;
42+
this._touched = false;
4143
}
4244

4345
get value(): any { return this._value; }
@@ -52,8 +54,14 @@ export class AbstractControl {
5254

5355
get dirty(): boolean { return !this.pristine; }
5456

57+
get touched(): boolean { return this._touched; }
58+
59+
get untouched(): boolean { return !this._touched; }
60+
5561
get valueChanges(): Observable { return this._valueChanges; }
5662

63+
touch(): void { this._touched = true; }
64+
5765
setParent(parent) { this._parent = parent; }
5866

5967
updateValidity({onlySelf}: {onlySelf?: boolean} = {}): void {

‎modules/angular2/test/forms/directives_spec.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class DummyControlValueAccessor implements ControlValueAccessor {
2929
writtenValue;
3030

3131
registerOnChange(fn) {}
32+
registerOnTouched(fn) {}
3233

3334
writeValue(obj: any): void { this.writtenValue = obj; }
3435
}

‎modules/angular2/test/forms/integration_spec.ts‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,32 @@ export function main() {
138138
});
139139
}));
140140

141+
it("should mark controls as touched after interacting with the DOM control",
142+
inject([TestBed, AsyncTestCompleter], (tb, async) => {
143+
var login = new Control("oldValue");
144+
var form = new ControlGroup({"login": login});
145+
var ctx = MyComp.create({form: form});
146+
147+
var t = `<div [ng-form-model]="form">
148+
<input type="text" ng-control="login">
149+
</div>`;
150+
151+
tb.createView(MyComp, {context: ctx, html: t})
152+
.then((view) => {
153+
view.detectChanges();
154+
155+
var loginEl = view.querySelector("input");
156+
157+
expect(login.touched).toBe(false);
158+
159+
dispatchEvent(loginEl, "blur");
160+
161+
expect(login.touched).toBe(true);
162+
163+
async.done();
164+
});
165+
}));
166+
141167
describe("different control types", () => {
142168
it("should support <input type=text>", inject([TestBed, AsyncTestCompleter], (tb, async) => {
143169
var ctx = MyComp.create({form: new ControlGroup({"text": new Control("old")})});
@@ -590,6 +616,7 @@ class WrappedValue implements ControlValueAccessor {
590616
writeValue(value) { this.value = `!${value}!`; }
591617

592618
registerOnChange(fn) { this.onChange = fn; }
619+
registerOnTouched(fn) {}
593620

594621
handleOnChange(value) { this.onChange(value.substring(1, value.length - 1)); }
595622
}

0 commit comments

Comments
 (0)