From 07baae8e4df095740076e63a1e82305d092bd02f Mon Sep 17 00:00:00 2001 From: Robin North Date: Fri, 2 Mar 2018 20:25:08 +0000 Subject: [PATCH] Fix form submission (#129) * Fix check for radio and checkbox inputs * Fix GET form submission * Add example forms for testing * Refactor query string building --- example/example.js | 3 +- example/forms.html | 138 ++++++++++++++++++++++++++++++++ example/index.html | 17 ++-- example/page2.html | 12 +-- example/page3.html | 12 +-- lib/proto/attach-form.js | 17 ++-- lib/send-request.js | 43 +++++++--- lib/util/update-query-string.js | 10 +++ 8 files changed, 214 insertions(+), 38 deletions(-) create mode 100644 example/forms.html create mode 100644 lib/util/update-query-string.js diff --git a/example/example.js b/example/example.js index f52f8bf..594b3a5 100644 --- a/example/example.js +++ b/example/example.js @@ -20,7 +20,8 @@ document.addEventListener("pjax:success", function() { document.addEventListener("DOMContentLoaded", function() { var pjax = new Pjax({ elements: [".js-Pjax"], - selectors: [".body"] + selectors: [".body", "title"], + cacheBust: true, // currentUrlFullReload: true, }) console.log("Pjax initialized.", pjax) diff --git a/example/forms.html b/example/forms.html new file mode 100644 index 0000000..eee4469 --- /dev/null +++ b/example/forms.html @@ -0,0 +1,138 @@ + + + + + Forms + + + + +
+

Forms

+ Hello. Try out the examples below and inspect the results in your browser's developer tools, or go to the Index. + +

GET form

+ +
+ + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ +
+ + + + + +
+ +
+
+ + + + +
+
+ + + + +
+
+ + +
+ +
+
+ +

POST form

+ +
+ + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ +
+ + + + + +
+ +
+
+ + + + +
+
+ + + + +
+
+ + +
+
+ + diff --git a/example/index.html b/example/index.html index f9e3da5..2245d5b 100644 --- a/example/index.html +++ b/example/index.html @@ -1,17 +1,22 @@ - + Hello - - + + -
+

Index

Hello. - Go to Page 2 or Page 3 and view your console to see Pjax events. - Clicking on this page will just reload the page entirely. + Go to Page 2 or Page 3 and view your console to see Pjax events. + Clicking on this page will just reload the page entirely. + +

Forms

+ + You can submit GET or POST forms with Pjax! Go to the form examples to try it out. +
diff --git a/example/page2.html b/example/page2.html index 36d2e33..7d0b941 100644 --- a/example/page2.html +++ b/example/page2.html @@ -1,15 +1,15 @@ - - Hello - - + + Page 2 + + -
+

Page 2

- Hello. Go to Index. + Hello. Go to Index.
diff --git a/example/page3.html b/example/page3.html index 7546dd1..9864eb6 100644 --- a/example/page3.html +++ b/example/page3.html @@ -1,15 +1,15 @@ - - Hello - - + + Page 3 + + -
+

Page 3

- Hello. Go to Index. + Hello. Go to Index.
diff --git a/lib/proto/attach-form.js b/lib/proto/attach-form.js index fa4d6af..3ca881b 100644 --- a/lib/proto/attach-form.js +++ b/lib/proto/attach-form.js @@ -11,7 +11,8 @@ var formAction = function(el, event) { // Initialize requestOptions options.requestOptions = { requestUrl: el.getAttribute("action") || window.location.href, - requestMethod: el.getAttribute("method") || "GET" + requestMethod: el.getAttribute("method") || "GET", + requestParams: [] } // create a testable virtual link of the form action @@ -44,24 +45,20 @@ var formAction = function(el, event) { event.preventDefault() - var paramObject = [] for (var elementKey in el.elements) { var element = el.elements[elementKey] // jscs:disable disallowImplicitTypeConversion if (!!element.name && element.attributes !== undefined && element.tagName.toLowerCase() !== "button") { // jscs:enable disallowImplicitTypeConversion - if ((element.attributes.type !== "checkbox" && element.attributes.type !== "radio") || element.checked) { - paramObject.push({name: encodeURIComponent(element.name), value: encodeURIComponent(element.value)}) + if ((!element.attributes.type || element.attributes.type.value !== "checkbox" && element.attributes.type.value !== "radio") || element.checked) { + options.requestOptions.requestParams.push({ + name: encodeURIComponent(element.name), + value: encodeURIComponent(element.value) + }) } } } - // Creating a getString - var paramsString = (paramObject.map(function(value) {return value.name + "=" + value.value})).join("&") - - options.requestOptions.requestPayload = paramObject - options.requestOptions.requestPayloadString = paramsString - el.setAttribute(attrClick, "submit") options.triggerElement = el diff --git a/lib/send-request.js b/lib/send-request.js index 618f65b..f2817b5 100644 --- a/lib/send-request.js +++ b/lib/send-request.js @@ -1,7 +1,11 @@ +var updateQueryString = require("./util/update-query-string"); + module.exports = function(location, options, callback) { options = options || {} - var requestMethod = options.requestMethod || "GET" - var requestPayload = options.requestPayloadString || null + var queryString + var requestMethod = (options.requestMethod || "GET").toUpperCase() + var requestParams = options.requestParams || null + var requestPayload = null var request = new XMLHttpRequest() request.onreadystatechange = function() { @@ -24,19 +28,40 @@ module.exports = function(location, options, callback) { callback(null, request) } - // Add a timestamp as part of the query string if cache busting is enabled - if (this.options.cacheBust) { - location += (!/[?&]/.test(location) ? "?" : "&") + new Date().getTime() + // Prepare the request payload for forms, if available + if (requestParams && requestParams.length) { + // Build query string + queryString = (requestParams.map(function(param) {return param.name + "=" + param.value})).join("&") + + switch (requestMethod) { + case "GET": + // Reset query string to avoid an issue with repeat submissions where checkboxes that were + // previously checked are incorrectly preserved + location = location.split("?")[0] + + // Append new query string + location += "?" + queryString + break + + case "POST": + // Send query string as request payload + requestPayload = queryString + break + } } - request.open(requestMethod.toUpperCase(), location, true) + // Add a timestamp as part of the query string if cache busting is enabled + if (this.options.cacheBust) { + location = updateQueryString(location, "t", Date.now()) + } + + request.open(requestMethod, location, true) request.timeout = options.timeout request.setRequestHeader("X-Requested-With", "XMLHttpRequest") request.setRequestHeader("X-PJAX", "true") - // Add the request payload if available - if (options.requestPayloadString !== undefined && options.requestPayloadString !== "") { - // Send the proper header information along with the request + // Send the proper header information for POST forms + if (requestPayload && requestMethod === "POST") { request.setRequestHeader("Content-type", "application/x-www-form-urlencoded") } diff --git a/lib/util/update-query-string.js b/lib/util/update-query-string.js new file mode 100644 index 0000000..3635de0 --- /dev/null +++ b/lib/util/update-query-string.js @@ -0,0 +1,10 @@ +module.exports = function(uri, key, value) { + var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i") + var separator = uri.indexOf("?") !== -1 ? "&" : "?" + if (uri.match(re)) { + return uri.replace(re, "$1" + key + "=" + value + "$2") + } + else { + return uri + separator + key + "=" + value + } +}