diff --git a/src/htmx.js b/src/htmx.js index 3ae640d2..7a3fef2b 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -1116,10 +1116,14 @@ var htmx = htmx || (function () { var encodedParameters = null; forEach(getExtensions(elt), function (extension) { if (encodedParameters == null) { - extension.encodeParameters(xhr, filteredParameters, elt); + encodedParameters = extension.encodeParameters(xhr, filteredParameters, elt); } }); - return urlEncode(filteredParameters); + if (encodedParameters != null) { + return encodedParameters; + } else { + return urlEncode(filteredParameters); + } } function issueAjaxRequest(elt, verb, path, eventTarget) { diff --git a/test/extensions/json-enc.js b/test/extensions/json-enc.js new file mode 100644 index 00000000..8dc25a62 --- /dev/null +++ b/test/extensions/json-enc.js @@ -0,0 +1,143 @@ +// +describe("json-enc extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + htmx.defineExtension('json-enc', { + encodeParameters : function(xhr, parameters, elt) { + xhr.requestHeaders['Content-Type'] = 'application/json'; + xhr.overrideMimeType('text/json'); + return (JSON.stringify(parameters)); + } + }); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('handles basic post properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("POST", "/test", jsonResponseBody); + var div = make("
click me
"); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles basic put properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("PUT", "/test", jsonResponseBody); + var div = make('
click me
'); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles basic patch properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("PATCH", "/test", jsonResponseBody); + var div = make('
click me
'); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles basic delete properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("DELETE", "/test", jsonResponseBody); + var div = make('
click me
'); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles post with form parameters', function () { + + this.server.respondWith("POST", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make('
' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + it('handles put with form parameters', function () { + + this.server.respondWith("PUT", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make(' ' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + + it('handles patch with form parameters', function () { + + this.server.respondWith("PATCH", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make(' ' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + it('handles delete with form parameters', function () { + + this.server.respondWith("DELETE", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make(' ' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + + +}); + diff --git a/test/index.html b/test/index.html index 66f6c172..fd667f66 100644 --- a/test/index.html +++ b/test/index.html @@ -87,7 +87,7 @@ - + diff --git a/www/docs.md b/www/docs.md index 605d3057..1ba7dbe1 100644 --- a/www/docs.md +++ b/www/docs.md @@ -97,7 +97,7 @@ It can be used via [NPM](https://www.npmjs.com/) as "`htmx.org`" or downloaded o ## [AJAX](#ajax) -The core feature of htmx is a set of attributes that allow you to issue AJAX requests directly from HTML: +The core of htmx is a set of attributes that allow you to issue AJAX requests directly from HTML: * [hx-get](/attributes/hx-get) - Issues a `GET` request to the given URL * [hx-post](/attributes/hx-post) - Issues a `POST` request to the given URL @@ -492,11 +492,13 @@ If you are interested in adding your own extension to htmx, please [see the exte Htmx offers some officially supported extensions that are tested against the htmx code base, including: -* [`debug`](/official-extensions#debug) - an extension for htmx debugging a particular element -* [`rails-method`](/official-extensions#rails-method) - an extension for including the `_method` parameter -[that rails uses](https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark). +* [`json-enc`](/official-extensions#json-enc) - use JSON encoding in the body of requests, rather than the default `x-www-form-urlencoded` * [`morphdom-swap`](/official-extensions#morphdom-swap) - an extension for using the [morphdom](https://github.com/patrick-steele-idem/morphdom) library as the swapping mechanism in htmx. +* [`debug`](/official-extensions#debug) - an extension for debugging of a particular element using htmx +* [`rails-method`](/official-extensions#rails-method) - an extension for including the `_method` parameter that +[that rails uses](https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark) +for non-`POST` or `GET` HTTP methods. See the [officially extensions](/official-extensions) page for a complete list. diff --git a/www/official-extensions.md b/www/official-extensions.md index 65c3333e..07e42658 100644 --- a/www/official-extensions.md +++ b/www/official-extensions.md @@ -93,4 +93,29 @@ htmx.defineExtension('morphdom-swap', { } } }); -``` \ No newline at end of file +``` + +### [`json-enc`](#json-enc) + +#### Description + +This extension encodes parameters in JSON format instead of url format. + +#### Usage + +```html +
click me
+``` + +#### Source + +```javascript +htmx.defineExtension('json-enc', { + encodeParameters : function(xhr, parameters, elt) { + xhr.requestHeaders['Content-Type'] = 'application/json'; + xhr.overrideMimeType('text/json'); + return (JSON.stringify(parameters)); + } +}); + +```