diff --git a/packages/forms/src/directives/ng_form.ts b/packages/forms/src/directives/ng_form.ts
index 431fa84d3908f..ae66484e01815 100644
--- a/packages/forms/src/directives/ng_form.ts
+++ b/packages/forms/src/directives/ng_form.ts
@@ -298,7 +298,9 @@ export class NgForm extends ControlContainer implements Form, AfterViewInit {
(this as {submitted: boolean}).submitted = true;
syncPendingControls(this.form, this._directives);
this.ngSubmit.emit($event);
- return false;
+ // Forms with `method="dialog"` have some special behavior
+ // that won't reload the page and that shouldn't be prevented.
+ return ($event?.target as HTMLFormElement | null)?.method === 'dialog';
}
/**
diff --git a/packages/forms/src/directives/reactive_directives/form_group_directive.ts b/packages/forms/src/directives/reactive_directives/form_group_directive.ts
index 3f19560ecfeaa..21ee9f6081c14 100644
--- a/packages/forms/src/directives/reactive_directives/form_group_directive.ts
+++ b/packages/forms/src/directives/reactive_directives/form_group_directive.ts
@@ -94,8 +94,8 @@ export class FormGroupDirective extends ControlContainer implements Form, OnChan
@Output() ngSubmit = new EventEmitter();
constructor(
- @Optional() @Self() @Inject(NG_VALIDATORS) private validators: (Validator|ValidatorFn)[],
- @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private asyncValidators:
+ @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[],
+ @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:
(AsyncValidator|AsyncValidatorFn)[]) {
super();
this._setValidators(validators);
@@ -271,7 +271,9 @@ export class FormGroupDirective extends ControlContainer implements Form, OnChan
(this as {submitted: boolean}).submitted = true;
syncPendingControls(this.form, this.directives);
this.ngSubmit.emit($event);
- return false;
+ // Forms with `method="dialog"` have some special behavior
+ // that won't reload the page and that shouldn't be prevented.
+ return ($event?.target as HTMLFormElement | null)?.method === 'dialog';
}
/**
diff --git a/packages/forms/test/reactive_integration_spec.ts b/packages/forms/test/reactive_integration_spec.ts
index b795ce5b9b153..7a0bc61fee504 100644
--- a/packages/forms/test/reactive_integration_spec.ts
+++ b/packages/forms/test/reactive_integration_spec.ts
@@ -7,7 +7,7 @@
*/
import {ɵgetDOM as getDOM} from '@angular/common';
-import {Component, Directive, forwardRef, Input, NgModule, OnDestroy, Type} from '@angular/core';
+import {Component, Directive, ElementRef, forwardRef, Input, NgModule, OnDestroy, Type, ViewChild} from '@angular/core';
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {AbstractControl, AsyncValidator, AsyncValidatorFn, COMPOSITION_BUFFER_MODE, ControlValueAccessor, DefaultValueAccessor, FormArray, FormBuilder, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormsModule, MaxValidator, MinLengthValidator, MinValidator, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, ReactiveFormsModule, Validator, Validators} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by';
@@ -2114,6 +2114,20 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');
expect(passwordControl.value).toEqual('Carson', 'Expected value to change on submit.');
expect(passwordControl.valid).toBe(true, 'Expected validation to run on submit.');
});
+
+ it('should not prevent the default action on forms with method="dialog"', fakeAsync(() => {
+ if (typeof HTMLDialogElement === 'undefined') {
+ return;
+ }
+
+ const fixture = initTest(NativeDialogForm);
+ fixture.detectChanges();
+ tick();
+ const event = dispatchEvent(fixture.componentInstance.form.nativeElement, 'submit');
+ fixture.detectChanges();
+
+ expect(event.defaultPrevented).toBe(false);
+ }));
});
});
@@ -5437,3 +5451,17 @@ class MinMaxFormControlComp {
min: number|string = 1;
max: number|string = 10;
}
+
+@Component({
+ template: `
+
+ `
+})
+class NativeDialogForm {
+ @ViewChild('form') form!: ElementRef;
+ formGroup = new FormGroup({});
+}
diff --git a/packages/forms/test/template_integration_spec.ts b/packages/forms/test/template_integration_spec.ts
index 5385d6e939776..96f797d129bf9 100644
--- a/packages/forms/test/template_integration_spec.ts
+++ b/packages/forms/test/template_integration_spec.ts
@@ -7,7 +7,7 @@
*/
import {CommonModule, ɵgetDOM as getDOM} from '@angular/common';
-import {Component, Directive, forwardRef, Input, Type, ViewChild} from '@angular/core';
+import {Component, Directive, ElementRef, forwardRef, Input, Type, ViewChild} from '@angular/core';
import {ComponentFixture, fakeAsync, TestBed, tick, waitForAsync} from '@angular/core/testing';
import {AbstractControl, AsyncValidator, COMPOSITION_BUFFER_MODE, ControlValueAccessor, FormControl, FormsModule, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgForm, NgModel, Validator} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by';
@@ -1062,6 +1062,21 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
['fired', 'fired'],
'Expected ngModelChanges to fire again on submit if value changed.');
}));
+
+
+ it('should not prevent the default action on forms with method="dialog"', fakeAsync(() => {
+ if (typeof HTMLDialogElement === 'undefined') {
+ return;
+ }
+
+ const fixture = initTest(NativeDialogForm);
+ fixture.detectChanges();
+ tick();
+ const event = dispatchEvent(fixture.componentInstance.form.nativeElement, 'submit');
+ fixture.detectChanges();
+
+ expect(event.defaultPrevented).toBe(false);
+ }));
});
describe('ngFormOptions', () => {
@@ -2932,3 +2947,17 @@ class NgModelNoMinMaxValidator {
max!: number;
@ViewChild('myDir') myDir: any;
}
+
+@Component({
+ selector: 'ng-model-nested',
+ template: `
+
+ `
+})
+class NativeDialogForm {
+ @ViewChild('form') form!: ElementRef;
+}
diff --git a/packages/platform-browser/testing/src/browser_util.ts b/packages/platform-browser/testing/src/browser_util.ts
index aa28e9563e14d..8acf2ac7c895d 100644
--- a/packages/platform-browser/testing/src/browser_util.ts
+++ b/packages/platform-browser/testing/src/browser_util.ts
@@ -97,10 +97,11 @@ export class BrowserDetection {
export const browserDetection: BrowserDetection = BrowserDetection.setup();
-export function dispatchEvent(element: any, eventType: any): void {
+export function dispatchEvent(element: any, eventType: any): Event {
const evt: Event = getDOM().getDefaultDocument().createEvent('Event');
evt.initEvent(eventType, true, true);
getDOM().dispatchEvent(element, evt);
+ return evt;
}
export function createMouseEvent(eventType: string): MouseEvent {