diff --git a/css/amp.css b/css/amp.css
index 8137bd3192b9..3743061af190 100644
--- a/css/amp.css
+++ b/css/amp.css
@@ -564,6 +564,7 @@ amp-iframe iframe {
/**
* Forms error/success messaging containers should be hidden at first.
*/
+form [submitting],
form [submit-success],
form [submit-error] {
display: none;
diff --git a/examples/forms.amp.html b/examples/forms.amp.html
index 529345e3bfb9..481e2d1236ec 100644
--- a/examples/forms.amp.html
+++ b/examples/forms.amp.html
@@ -264,7 +264,11 @@
Subscribe to our weekly Newsletter (POST xhr same origin)
-
+
+
+ Please wait, {{name}}.
+
+
Success! Thanks for subscribing! Please make sure to check your email
to confirm!
diff --git a/extensions/amp-form/0.1/amp-form.css b/extensions/amp-form/0.1/amp-form.css
index 98b3f5268fd6..40fa2ea5ff63 100644
--- a/extensions/amp-form/0.1/amp-form.css
+++ b/extensions/amp-form/0.1/amp-form.css
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+form.amp-form-submitting [submitting],
form.amp-form-submit-success [submit-success],
form.amp-form-submit-error [submit-error] {
display: block;
diff --git a/extensions/amp-form/0.1/amp-form.js b/extensions/amp-form/0.1/amp-form.js
index c886e42948e4..9bef1e5523fa 100644
--- a/extensions/amp-form/0.1/amp-form.js
+++ b/extensions/amp-form/0.1/amp-form.js
@@ -443,10 +443,12 @@ export class AmpForm {
*/
doXhr_(opt_extraValues) {
const isHeadOrGet = this.method_ == 'GET' || this.method_ == 'HEAD';
+ const values = this.getFormAsObject_(opt_extraValues);
+ this.renderTemplate_(values);
+
let xhrUrl, body;
if (isHeadOrGet) {
- xhrUrl = addParamsToUrl(dev().assertString(this.xhrAction_),
- this.getFormAsObject_(opt_extraValues));
+ xhrUrl = addParamsToUrl(dev().assertString(this.xhrAction_), values);
} else {
xhrUrl = this.xhrAction_;
body = new FormData(this.form_);
@@ -454,6 +456,7 @@ export class AmpForm {
body.append(key, opt_extraValues[key]);
}
}
+
return this.xhr_.fetch(dev().assertString(xhrUrl), {
body,
method: this.method_,
@@ -474,6 +477,7 @@ export class AmpForm {
return response.json().then(json => {
this.triggerAction_(/* success */ true, json);
this.analyticsEvent_('amp-form-submit-success');
+ this.cleanupRenderedTemplate_();
this.setState_(FormState_.SUBMIT_SUCCESS);
this.renderTemplate_(json || {});
this.maybeHandleRedirect_(response);
@@ -492,6 +496,7 @@ export class AmpForm {
this.triggerAction_(
/* success */ false, errorResponse ? errorResponse.responseJson : null);
this.analyticsEvent_('amp-form-submit-error');
+ this.cleanupRenderedTemplate_();
this.setState_(FormState_.SUBMIT_ERROR);
this.renderTemplate_(errorResponse.responseJson || {});
this.maybeHandleRedirect_(errorResponse.response);
@@ -604,11 +609,11 @@ export class AmpForm {
/**
* Returns form data as an object.
* @param {!Object=} opt_extraFields
- * @return {!Object}
+ * @return {!JSONType}
* @private
*/
getFormAsObject_(opt_extraFields) {
- const data = {};
+ const data = /** @type {!JSONType} */ ({});
const inputs = this.form_.elements;
const submittableTagsRegex = /^(?:input|select|textarea)$/i;
const unsubmittableTypesRegex = /^(?:button|image|file|reset)$/i;
diff --git a/validator/validator-main.protoascii b/validator/validator-main.protoascii
index 47908ac206bd..3c8df91a2bdc 100644
--- a/validator/validator-main.protoascii
+++ b/validator/validator-main.protoascii
@@ -2578,6 +2578,17 @@ tags { #