diff options
author | Dave Ashby <dave.ashby@potomacsi.com> | 2023-09-14 12:41:32 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-14 10:41:32 -0600 |
commit | e3a68c5e1bfda45e17ee17ff06555f9c027c9a00 (patch) | |
tree | ef91dce39637cb727bb421532519438c59f1798b | |
parent | 89f13e6cf25de064b698fac987c96e45e7d65379 (diff) | |
download | htmx-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.js | 30 | ||||
-rw-r--r-- | test/ext/client-side-templates.js | 18 | ||||
-rw-r--r-- | www/content/extensions/client-side-templates.md | 35 |
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 |