summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDave Ashby <dave.ashby@potomacsi.com>2023-09-14 12:41:32 -0400
committerGitHub <noreply@github.com>2023-09-14 10:41:32 -0600
commite3a68c5e1bfda45e17ee17ff06555f9c027c9a00 (patch)
treeef91dce39637cb727bb421532519438c59f1798b
parent89f13e6cf25de064b698fac987c96e45e7d65379 (diff)
downloadhtmx-e3a68c5e1bfda45e17ee17ff06555f9c027c9a00.tar.gz
htmx-e3a68c5e1bfda45e17ee17ff06555f9c027c9a00.zip
Add support for array templates to the client-side templates extension (#1776)
Fix client-side template support for arrays (non-breaking) Co-authored-by: Dave Ashby <david.h.ashby.ctr@nga.mil>
-rw-r--r--src/ext/client-side-templates.js30
-rw-r--r--test/ext/client-side-templates.js18
-rw-r--r--www/content/extensions/client-side-templates.md35
3 files changed, 82 insertions, 1 deletions
diff --git a/src/ext/client-side-templates.js b/src/ext/client-side-templates.js
index 9d47d270..f96feb6e 100644
--- a/src/ext/client-side-templates.js
+++ b/src/ext/client-side-templates.js
@@ -13,6 +13,18 @@ htmx.defineExtension('client-side-templates', {
}
}
+ var mustacheArrayTemplate = htmx.closest(elt, "[mustache-array-template]");
+ if (mustacheArrayTemplate) {
+ var data = JSON.parse(text);
+ var templateId = mustacheArrayTemplate.getAttribute('mustache-array-template');
+ var template = htmx.find("#" + templateId);
+ if (template) {
+ return Mustache.render(template.innerHTML, {"data": data });
+ } else {
+ throw "Unknown mustache template: " + templateId;
+ }
+ }
+
var handlebarsTemplate = htmx.closest(elt, "[handlebars-template]");
if (handlebarsTemplate) {
var data = JSON.parse(text);
@@ -20,6 +32,13 @@ htmx.defineExtension('client-side-templates', {
return Handlebars.partials[templateName](data);
}
+ var handlebarsArrayTemplate = htmx.closest(elt, "[handlebars-array-template]");
+ if (handlebarsArrayTemplate) {
+ var data = JSON.parse(text);
+ var templateName = handlebarsArrayTemplate.getAttribute('handlebars-array-template');
+ return Handlebars.partials[templateName]({"data": data});
+ }
+
var nunjucksTemplate = htmx.closest(elt, "[nunjucks-template]");
if (nunjucksTemplate) {
var data = JSON.parse(text);
@@ -32,6 +51,17 @@ htmx.defineExtension('client-side-templates', {
}
}
+ var nunjucksArrayTemplate = htmx.closest(elt, "[nunjucks-array-template]");
+ if (nunjucksArrayTemplate) {
+ var data = JSON.parse(text);
+ var templateName = nunjucksArrayTemplate.getAttribute('nunjucks-array-template');
+ var template = htmx.find('#' + templateName);
+ if (template) {
+ return nunjucks.renderString(template.innerHTML, {"data": data});
+ } else {
+ return nunjucks.render(templateName, {"data": data});
+ }
+ }
return text;
}
});
diff --git a/test/ext/client-side-templates.js b/test/ext/client-side-templates.js
index 29206d21..28bf09f9 100644
--- a/test/ext/client-side-templates.js
+++ b/test/ext/client-side-templates.js
@@ -17,6 +17,15 @@ describe("client-side-templates extension", function() {
btn.innerHTML.should.equal("*bar*");
});
+ it('works on mustache array template', function () {
+ this.server.respondWith("GET", "/test", '{"foo":"bar"}');
+ var btn = make('<button hx-get="/test" hx-ext="client-side-templates" mustache-array-template="mt1">Click Me!</button>')
+ make('<script id="mt1" type="x-tmpl-mustache">*{{data.foo}}*</script>')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("*bar*");
+ });
+
it('works on basic handlebars template', function () {
this.server.respondWith("GET", "/test", '{"foo":"bar"}');
var btn = make('<button hx-get="/test" hx-ext="client-side-templates" handlebars-template="hb1">Click Me!</button>')
@@ -26,5 +35,12 @@ describe("client-side-templates extension", function() {
btn.innerHTML.should.equal("*bar*");
});
-
+ it('works on handlebars array template', function () {
+ this.server.respondWith("GET", "/test", '{"foo":"bar"}');
+ var btn = make('<button hx-get="/test" hx-ext="client-side-templates" handlebars-array-template="hb1">Click Me!</button>')
+ Handlebars.partials["hb1"] = Handlebars.compile("*{{data.foo}}*");
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("*bar*");
+ });
}); \ No newline at end of file
diff --git a/www/content/extensions/client-side-templates.md b/www/content/extensions/client-side-templates.md
index bfa8a885..7a5cfed9 100644
--- a/www/content/extensions/client-side-templates.md
+++ b/www/content/extensions/client-side-templates.md
@@ -19,6 +19,8 @@ the template the standard way for that template engine:
The AJAX response body will be parsed as JSON and passed into the template rendering.
+A second "array" version of each template is now offered, which is particularly helpful for APIs that return arrays of data. These templates are referenced as `<template-engine>-array-template`, and the data is accessed as `data.my_server_field`. At least in the case of `mustache`, it also enables use of loops using the `{{#data}} my_server_field {{/data}}` syntax.
+
## Install
```html
@@ -85,6 +87,39 @@ If you wish to put a template into another file, you can use a directive such as
Here is a [jsbin](https://jsbin.com/qonutovico/edit?html,output) playground to try this out.
+Here's a working example using the `mustache-array-template` working against an API that returns an array:
+```html
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <title>JS Bin</title>
+ <script src="https://unpkg.com/htmx.org"></script>
+ <script src="https://unpkg.com/htmx.org/dist/ext/client-side-templates.js"></script>
+ <script src="https://unpkg.com/mustache@latest"></script>
+</head>
+<body>
+ <div hx-ext="client-side-templates">
+ <button hx-get="https://jsonplaceholder.typicode.com/users"
+ hx-swap="innerHTML"
+ hx-target="#content"
+ mustache-array-template="foo">
+ Click Me
+ </button>
+
+ <p id="content">Start</p>
+
+ <template id="foo">
+ {{#data}}
+ <p> {{name}} at {{email}} is with {{company.name}}</p>
+ {{/data}}
+ </template>
+ </div>
+</body>
+</html>
+```
+
## CORS and REST/JSON
As a warning, many web services use CORS protection and/or other protection schemes to reject a