Skip to content

Commit

Permalink
Extend client-side-templates to handle XSLT transformations (#1210)
Browse files Browse the repository at this point in the history
extend client-side-templates to handle XSLT transformations
  • Loading branch information
jyrimatti authored Sep 19, 2023
1 parent af0dc9c commit 048f98c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 7 deletions.
19 changes: 18 additions & 1 deletion src/ext/client-side-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,24 @@ htmx.defineExtension('client-side-templates', {
} else {
return nunjucks.render(templateName, data);
}
}
}

var xsltTemplate = htmx.closest(elt, "[xslt-template]");
if (xsltTemplate) {
var templateId = xsltTemplate.getAttribute('xslt-template');
var template = htmx.find("#" + templateId);
if (template) {
var content = template.innerHTML ? new DOMParser().parseFromString(template.innerHTML, 'application/xml')
: template.contentDocument;
var processor = new XSLTProcessor();
processor.importStylesheet(content);
var data = new DOMParser().parseFromString(text, "application/xml");
var frag = processor.transformToFragment(data, document);
return new XMLSerializer().serializeToString(frag);
} else {
throw "Unknown XSLT template: " + templateId;
}
}

var nunjucksArrayTemplate = htmx.closest(elt, "[nunjucks-array-template]");
if (nunjucksArrayTemplate) {
Expand Down
13 changes: 13 additions & 0 deletions test/ext/client-side-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,17 @@ describe("client-side-templates extension", function() {
this.server.respond();
btn.innerHTML.should.equal("*bar*");
});

it('works on basic xslt template', function () {
this.server.respondWith("GET", "/test", '<foo>bar</foo>');
var btn = make('<button hx-get="/test" hx-ext="client-side-templates" xslt-template="mt1">Click Me!</button>')
make('<script id="mt1" type="application/xml">' +
`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">*<xsl:value-of select="foo" />*</xsl:template>
</xsl:stylesheet>
` + '</script>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("*bar*");
});
});
2 changes: 1 addition & 1 deletion www/content/extensions/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ See the individual extension documentation for more details.
| [`ajax-header`](@/extensions/ajax-header.md) | includes the commonly-used `X-Requested-With` header that identifies ajax requests in many backend frameworks
| [`alpine-morph`](@/extensions/alpine-morph.md) | an extension for using the [Alpine.js morph](https://alpinejs.dev/plugins/morph) plugin as the swapping mechanism in htmx.
| [`class-tools`](@/extensions/class-tools.md) | an extension for manipulating timed addition and removal of classes on HTML elements
| [`client-side-templates`](@/extensions/client-side-templates.md) | support for client side template processing of JSON responses
| [`client-side-templates`](@/extensions/client-side-templates.md) | support for client side template processing of JSON/XML responses
| [`debug`](@/extensions/debug.md) | an extension for debugging of a particular element using htmx
| [`disable-element`](@/extensions/disable-element.md) | an extension for disabling an element during an htmx request
| [`event-header`](@/extensions/event-header.md) | includes a JSON serialized version of the triggering event, if any
Expand Down
59 changes: 54 additions & 5 deletions www/content/extensions/client-side-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@
title = "client-side-templates"
+++

This extension supports transforming a JSON request response into HTML via a client-side template before it is
swapped into the DOM. Currently three client-side templating engines are supported:
This extension supports transforming a JSON/XML request response into HTML via a client-side template before it is
swapped into the DOM. Currently four client-side templating engines are supported:

* [mustache](https://github.com/janl/mustache.js)
* [handlebars](https://handlebarsjs.com/)
* [nunjucks](https://mozilla.github.io/nunjucks/)
* [xslt](https://developer.mozilla.org/en-US/docs/Web/XSLT)

When you add this extension on an element, any element below it in the DOM can use one of three attributes named
When you add this extension on an element, any element below it in the DOM can use one of four attributes named
`<template-engine>-template` (e.g. `mustache-template`) with a template ID, and the extension will resolve and render
the template the standard way for that template engine:

* `mustache` - looks a mustache &lt;script> tag up by ID for the template content
* `handlebars` - looks in the `Handlebars.partials` collection for a template with that name
* `nunjucks` - resolves the template by name via `nunjucks.render(<template-name>)
* `xslt` - looks an XSLT &lt;script> tag up by ID for the template content

The AJAX response body will be parsed as JSON and passed into the template rendering.
The AJAX response body will be parsed as JSON/XML 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.

Expand All @@ -43,10 +45,14 @@ A second "array" version of each template is now offered, which is particularly
nunjucks-template="my-nunjucks-template">
Handle with nunjucks
</button>
<button hx-get="/some_xml"
xslt-template="my-xslt-template">
Handle with XSLT
</button>
</div>
```

## Full HTML Example
### Full Mustache HTML Example

To use the client side template, you will need to include htmx, the extension, and the rendering engine.
Here is an example of this setup for Mustache using
Expand Down Expand Up @@ -120,6 +126,49 @@ Here's a working example using the `mustache-array-template` working against an
</html>
```

### Full XSLT HTML Example

To use the client side template, you will need to include htmx and the extension.
Here is an example of this setup for XSLT using a [`<script>` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script).

If you wish to put a template into another file, you can use a directive such as
`<object id="template-id" data="my-template.xml" style="position: absolute; bottom: 0px; width: 0px; height: 0px;">`.
Some styling is needed to keep the object visible while not taking any space.

```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>
</head>
<body>
<div hx-ext="client-side-templates">
<button hx-get="http://restapi.adequateshop.com/api/Traveler"
hx-swap="innerHTML"
hx-target="#content"
xslt-template="foo">
Click Me
</button>

<p id="content">Start</p>

<script id="foo" type="application/xml">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
page <xsl:value-of select="/TravelerinformationResponse/page" /> of <xsl:value-of select="/TravelerinformationResponse/total_pages" />
</xsl:template>
</xsl:stylesheet>
</script>
</div>
</body>
</html>
```


## CORS and REST/JSON

As a warning, many web services use CORS protection and/or other protection schemes to reject a
Expand Down

0 comments on commit 048f98c

Please sign in to comment.