diff --git a/modules/@angular/forms/src/directives/shared.ts b/modules/@angular/forms/src/directives/shared.ts
index 16a8c2a323..bb8b7aa1b0 100644
--- a/modules/@angular/forms/src/directives/shared.ts
+++ b/modules/@angular/forms/src/directives/shared.ts
@@ -45,8 +45,8 @@ export function setUpControl(control: FormControl, dir: NgControl): void {
// view -> model
dir.valueAccessor.registerOnChange((newValue: any) => {
dir.viewToModelUpdate(newValue);
- control.updateValue(newValue, {emitModelToViewChange: false});
control.markAsDirty();
+ control.updateValue(newValue, {emitModelToViewChange: false});
});
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
diff --git a/modules/@angular/forms/src/model.ts b/modules/@angular/forms/src/model.ts
index 1edd2c5295..821c72eb22 100644
--- a/modules/@angular/forms/src/model.ts
+++ b/modules/@angular/forms/src/model.ts
@@ -409,9 +409,9 @@ export class FormControl extends AbstractControl {
}
reset(value: any = null, {onlySelf}: {onlySelf?: boolean} = {}): void {
- this.updateValue(value, {onlySelf: onlySelf});
this.markAsPristine({onlySelf: onlySelf});
this.markAsUntouched({onlySelf: onlySelf});
+ this.updateValue(value, {onlySelf: onlySelf});
}
/**
diff --git a/modules/@angular/forms/test/reactive_integration_spec.ts b/modules/@angular/forms/test/reactive_integration_spec.ts
index 3d9276c9dd..0316084a40 100644
--- a/modules/@angular/forms/test/reactive_integration_spec.ts
+++ b/modules/@angular/forms/test/reactive_integration_spec.ts
@@ -236,6 +236,33 @@ export function main() {
});
}));
+ it('should mark controls as dirty before emitting a value change event',
+ inject(
+ [TestComponentBuilder, AsyncTestCompleter],
+ (tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
+ const login = new FormControl('oldValue');
+ const form = new FormGroup({'login': login});
+
+ const t = `
+
+
`;
+
+ tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
+ fixture.debugElement.componentInstance.form = form;
+ fixture.detectChanges();
+
+ login.valueChanges.subscribe(() => {
+ expect(login.dirty).toBe(true);
+ async.done();
+ });
+
+ const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
+ loginEl.value = 'newValue';
+
+ dispatchEvent(loginEl, 'input');
+ });
+ }));
+
it('should clear value in UI when form resets programmatically',
inject(
[TestComponentBuilder, AsyncTestCompleter],
@@ -289,6 +316,37 @@ export function main() {
});
}));
+ it('should mark control as pristine before emitting a value change event when resetting ',
+ inject(
+ [TestComponentBuilder, AsyncTestCompleter],
+ (tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
+ const login = new FormControl('oldValue');
+ const form = new FormGroup({'login': login});
+
+ const t = `
+
+
`;
+
+ tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
+ fixture.debugElement.componentInstance.form = form;
+ fixture.detectChanges();
+
+ const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
+ loginEl.value = 'newValue';
+
+ dispatchEvent(loginEl, 'input');
+
+ expect(login.pristine).toBe(false);
+
+ login.valueChanges.subscribe(() => {
+ expect(login.pristine).toBe(true);
+ async.done();
+ });
+
+ form.reset();
+ });
+ }));
+
it('should support form arrays',
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]);
diff --git a/modules/@angular/forms/test/template_integration_spec.ts b/modules/@angular/forms/test/template_integration_spec.ts
index 75eec3e60d..0f712099e3 100644
--- a/modules/@angular/forms/test/template_integration_spec.ts
+++ b/modules/@angular/forms/test/template_integration_spec.ts
@@ -369,6 +369,58 @@ export function main() {
});
}));
+ it('should mark controls as dirty before emitting a value change event',
+ fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+
+ const t = ``;
+
+ tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
+ fixture.detectChanges();
+
+ const form = fixture.debugElement.children[0].injector.get(NgForm).form;
+ fixture.detectChanges();
+ tick();
+
+ form.find('login').valueChanges.subscribe(
+ () => { expect(form.find('login').dirty).toBe(true); });
+
+ const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
+ loginEl.value = 'newValue';
+
+ dispatchEvent(loginEl, 'input');
+ });
+ })));
+
+ it('should mark control as pristine before emitting a value change event when resetting ',
+ fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+
+ const t = ``;
+
+ tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
+ fixture.detectChanges();
+
+ const form = fixture.debugElement.children[0].injector.get(NgForm).form;
+ const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
+ const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
+ fixture.detectChanges();
+ tick();
+
+ loginEl.value = 'newValue';
+ dispatchEvent(loginEl, 'input');
+
+ expect(form.find('login').pristine).toBe(false);
+
+ form.find('login').valueChanges.subscribe(
+ () => { expect(form.find('login').pristine).toBe(true); });
+
+ dispatchEvent(formEl, 'reset');
+ });
+ })));
+
describe('radio value accessor', () => {
it('should support ',
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {