feat(forms): added touched and untouched to Control
This commit is contained in:
parent
f303f0c17a
commit
ec3a78289f
|
@ -17,15 +17,17 @@ import {ControlValueAccessor} from './control_value_accessor';
|
||||||
@Directive({
|
@Directive({
|
||||||
selector:
|
selector:
|
||||||
'input[type=checkbox][ng-control],input[type=checkbox][ng-form-control],input[type=checkbox][ng-model]',
|
'input[type=checkbox][ng-control],input[type=checkbox][ng-form-control],input[type=checkbox][ng-model]',
|
||||||
hostListeners: {'change': 'onChange($event.target.checked)'},
|
hostListeners: {'change': 'onChange($event.target.checked)', 'blur': 'onTouched()'},
|
||||||
hostProperties: {'checked': 'checked'}
|
hostProperties: {'checked': 'checked'}
|
||||||
})
|
})
|
||||||
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
onChange: Function;
|
onChange: Function;
|
||||||
|
onTouched: Function;
|
||||||
|
|
||||||
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
|
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
|
this.onTouched = (_) => {};
|
||||||
cd.valueAccessor = this;
|
cd.valueAccessor = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,5 +36,6 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||||
this._elementRef.boundElementIndex, 'checked', value)
|
this._elementRef.boundElementIndex, 'checked', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(fn) { this.onChange = fn; }
|
registerOnChange(fn): void { this.onChange = fn; }
|
||||||
|
registerOnTouched(fn): void { this.onTouched = fn; }
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
export interface ControlValueAccessor {
|
export interface ControlValueAccessor {
|
||||||
writeValue(obj: any): void;
|
writeValue(obj: any): void;
|
||||||
registerOnChange(fun: any): void;
|
registerOnChange(fn: any): void;
|
||||||
|
registerOnTouched(fn: any): void;
|
||||||
}
|
}
|
|
@ -19,16 +19,21 @@ import {ControlValueAccessor} from './control_value_accessor';
|
||||||
@Directive({
|
@Directive({
|
||||||
selector:
|
selector:
|
||||||
'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]',
|
'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]',
|
||||||
hostListeners:
|
hostListeners: {
|
||||||
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
'change': 'onChange($event.target.value)',
|
||||||
|
'input': 'onChange($event.target.value)',
|
||||||
|
'blur': 'onTouched()'
|
||||||
|
},
|
||||||
hostProperties: {'value': 'value'}
|
hostProperties: {'value': 'value'}
|
||||||
})
|
})
|
||||||
export class DefaultValueAccessor implements ControlValueAccessor {
|
export class DefaultValueAccessor implements ControlValueAccessor {
|
||||||
value = null;
|
value = null;
|
||||||
onChange: Function;
|
onChange: Function;
|
||||||
|
onTouched: Function;
|
||||||
|
|
||||||
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
|
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
|
this.onTouched = (_) => {};
|
||||||
cd.valueAccessor = this;
|
cd.valueAccessor = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,5 +42,6 @@ export class DefaultValueAccessor implements ControlValueAccessor {
|
||||||
this._elementRef.boundElementIndex, 'value', value)
|
this._elementRef.boundElementIndex, 'value', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(fn) { this.onChange = fn; }
|
registerOnChange(fn): void { this.onChange = fn; }
|
||||||
|
registerOnTouched(fn): void { this.onTouched = fn; }
|
||||||
}
|
}
|
|
@ -18,16 +18,21 @@ import {ControlValueAccessor} from './control_value_accessor';
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'select[ng-control],select[ng-form-control],select[ng-model]',
|
selector: 'select[ng-control],select[ng-form-control],select[ng-model]',
|
||||||
hostListeners:
|
hostListeners: {
|
||||||
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
'change': 'onChange($event.target.value)',
|
||||||
|
'input': 'onChange($event.target.value)',
|
||||||
|
'blur': 'onTouched()'
|
||||||
|
},
|
||||||
hostProperties: {'value': 'value'}
|
hostProperties: {'value': 'value'}
|
||||||
})
|
})
|
||||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||||
value = null;
|
value = null;
|
||||||
onChange: Function;
|
onChange: Function;
|
||||||
|
onTouched: Function;
|
||||||
|
|
||||||
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
|
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
|
this.onTouched = (_) => {};
|
||||||
this.value = '';
|
this.value = '';
|
||||||
cd.valueAccessor = this;
|
cd.valueAccessor = this;
|
||||||
}
|
}
|
||||||
|
@ -37,5 +42,6 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||||
this._elementRef.boundElementIndex, 'value', value)
|
this._elementRef.boundElementIndex, 'value', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(fn) { this.onChange = fn; }
|
registerOnChange(fn): void { this.onChange = fn; }
|
||||||
|
registerOnTouched(fn): void { this.onTouched = fn; }
|
||||||
}
|
}
|
|
@ -27,6 +27,9 @@ export function setUpControl(c: Control, dir: ControlDirective) {
|
||||||
|
|
||||||
// model -> view
|
// model -> view
|
||||||
c.registerOnChange(newValue => dir.valueAccessor.writeValue(newValue));
|
c.registerOnChange(newValue => dir.valueAccessor.writeValue(newValue));
|
||||||
|
|
||||||
|
// touched
|
||||||
|
dir.valueAccessor.registerOnTouched(() => c.touch());
|
||||||
}
|
}
|
||||||
|
|
||||||
function _throwError(dir: ControlDirective, message: string): void {
|
function _throwError(dir: ControlDirective, message: string): void {
|
||||||
|
|
|
@ -30,6 +30,7 @@ export class AbstractControl {
|
||||||
_status: string;
|
_status: string;
|
||||||
_errors: StringMap<string, any>;
|
_errors: StringMap<string, any>;
|
||||||
_pristine: boolean;
|
_pristine: boolean;
|
||||||
|
_touched: boolean;
|
||||||
_parent: any; /* ControlGroup | ControlArray */
|
_parent: any; /* ControlGroup | ControlArray */
|
||||||
validator: Function;
|
validator: Function;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ export class AbstractControl {
|
||||||
constructor(validator: Function) {
|
constructor(validator: Function) {
|
||||||
this.validator = validator;
|
this.validator = validator;
|
||||||
this._pristine = true;
|
this._pristine = true;
|
||||||
|
this._touched = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(): any { return this._value; }
|
get value(): any { return this._value; }
|
||||||
|
@ -52,8 +54,14 @@ export class AbstractControl {
|
||||||
|
|
||||||
get dirty(): boolean { return !this.pristine; }
|
get dirty(): boolean { return !this.pristine; }
|
||||||
|
|
||||||
|
get touched(): boolean { return this._touched; }
|
||||||
|
|
||||||
|
get untouched(): boolean { return !this._touched; }
|
||||||
|
|
||||||
get valueChanges(): Observable { return this._valueChanges; }
|
get valueChanges(): Observable { return this._valueChanges; }
|
||||||
|
|
||||||
|
touch(): void { this._touched = true; }
|
||||||
|
|
||||||
setParent(parent) { this._parent = parent; }
|
setParent(parent) { this._parent = parent; }
|
||||||
|
|
||||||
updateValidity({onlySelf}: {onlySelf?: boolean} = {}): void {
|
updateValidity({onlySelf}: {onlySelf?: boolean} = {}): void {
|
||||||
|
|
|
@ -29,6 +29,7 @@ class DummyControlValueAccessor implements ControlValueAccessor {
|
||||||
writtenValue;
|
writtenValue;
|
||||||
|
|
||||||
registerOnChange(fn) {}
|
registerOnChange(fn) {}
|
||||||
|
registerOnTouched(fn) {}
|
||||||
|
|
||||||
writeValue(obj: any): void { this.writtenValue = obj; }
|
writeValue(obj: any): void { this.writtenValue = obj; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,6 +138,32 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it("should mark controls as touched after interacting with the DOM control",
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
var login = new Control("oldValue");
|
||||||
|
var form = new ControlGroup({"login": login});
|
||||||
|
var ctx = MyComp.create({form: form});
|
||||||
|
|
||||||
|
var t = `<div [ng-form-model]="form">
|
||||||
|
<input type="text" ng-control="login">
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
tb.createView(MyComp, {context: ctx, html: t})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
var loginEl = view.querySelector("input");
|
||||||
|
|
||||||
|
expect(login.touched).toBe(false);
|
||||||
|
|
||||||
|
dispatchEvent(loginEl, "blur");
|
||||||
|
|
||||||
|
expect(login.touched).toBe(true);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
describe("different control types", () => {
|
describe("different control types", () => {
|
||||||
it("should support <input type=text>", inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
it("should support <input type=text>", inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
var ctx = MyComp.create({form: new ControlGroup({"text": new Control("old")})});
|
var ctx = MyComp.create({form: new ControlGroup({"text": new Control("old")})});
|
||||||
|
@ -590,6 +616,7 @@ class WrappedValue implements ControlValueAccessor {
|
||||||
writeValue(value) { this.value = `!${value}!`; }
|
writeValue(value) { this.value = `!${value}!`; }
|
||||||
|
|
||||||
registerOnChange(fn) { this.onChange = fn; }
|
registerOnChange(fn) { this.onChange = fn; }
|
||||||
|
registerOnTouched(fn) {}
|
||||||
|
|
||||||
handleOnChange(value) { this.onChange(value.substring(1, value.length - 1)); }
|
handleOnChange(value) { this.onChange(value.substring(1, value.length - 1)); }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue