Skip to content

Commit 32da210

Browse files
author
Marco Zocca
committed
v 0.3
1 parent ce8f889 commit 32da210

File tree

6 files changed

+40
-18
lines changed

6 files changed

+40
-18
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# 0.3
2+
3+
* the HTMX swap mechanism works once more as expected: this extension now receives an *object* from the server, which
4+
is unpacked into Plotly restyle data and HTML markup.
5+
6+
# 0.2
7+
8+
* use [`plotly_utils.py`](https://cdn.jsdelivr.net/gh/ocramz/htmx-plotly@0.2/plotly_utils.py) to convert between
9+
Plotly objects and restyle-friendly JSON.

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Possible applications include: simple dashboards, data apps, and similar.
1111
Load the script from CDN into the head of your HTML file:
1212

1313
```html
14-
<script src="https://cdn.jsdelivr.net/gh/ocramz/htmx-plotly@0.2/htmx-plotly.js" integrity="sha256-0lbEDYe4H+Z2f/YMKEgbTnyvT2Wa837+a+D7XaPcKIo=" crossorigin="anonymous"></script>```
14+
1515
```
1616

1717
and of course also HTMX and Plotly:
@@ -26,15 +26,17 @@ and of course also HTMX and Plotly:
2626
Add these attributes to a page element:
2727
* `hx-ext="htmx-plotly"` means this element uses the extension
2828
* `hx-post="/get-data"` the HTTP endpoint that returns the new plot data
29-
* `hx-swap="none"` don't mutate the DOM with the result (we need to call the Plotly API instead)
3029
* `plot-id="my-plot"` ID of the DOM element that hosts the Plotly chart.
3130

3231
Example: here we make an `<a>` text link trigger the update of the plot within element `my-plot`:
3332

3433
```html
35-
<a href="#" hx-ext="htmx-plotly" hx-post="/get-data" hx-swap="none" plot-id="my-plot"><h1>UPDATE</h1></a>
34+
<a href="#" hx-ext="htmx-plotly" hx-post="/get-data" plot-id="my-plot"><h1>UPDATE</h1></a>
3635
```
3736

37+
NB: As of `v0.3` the HTMX swap mechanism works once more as expected: this extension now receives an *object* from the server, which
38+
is unpacked into Plotly restyle data and HTML markup.
39+
3840
### Setup (frontend)
3941

4042
Plotly charts need an empty div element as well as a script tag for initialization:

htmx-plotly.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
// adapted from https://unpkg.com/htmx.org@1.9.10/dist/ext/client-side-templates.js
22

3+
function htmlUnescape(input) {
4+
// // HTML-in-JSON should be escaped on the wire, and decoded safely here with DOMParser to avoid XSS
5+
var doc = new DOMParser().parseFromString(input, "text/html");
6+
return doc.documentElement.textContent
7+
}
8+
39
htmx.defineExtension('htmx-plotly', {
410
transformResponse : (text, xhr, elt) => {
511

612
const verbose = true
713
const pedantic = false
814

915
var plotEl = htmx.closest(elt, "[plot-id]"); // closest including div element
16+
const payload = JSON.parse(text)
17+
const dataNew = payload['restyle_data']
18+
n = dataNew.length
19+
const markup = htmlUnescape(payload['markup']) // to be passed back to HTMX for swapping
1020
if (plotEl) {
11-
const dataNew = JSON.parse(text)
12-
n = dataNew.length
1321
const plotId = plotEl.getAttribute('plot-id'); // lookup value of ? in < .. plot-id="?">
1422
var plotDiv = htmx.find("#" + plotId); // div element pointed at
1523
if (plotDiv) {
@@ -31,7 +39,7 @@ htmx.defineExtension('htmx-plotly', {
3139
console.log('No plot-id attribute defined')
3240
}
3341

34-
return ''
42+
return markup
3543

3644
}
3745
}

index.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010

1111
<!-- <a href="#" hx-ext="htmx-plotly" hx-post="/get-data" hx-swap="none" plot-id="my-plot"><h1>UPDATE</h1></a> -->
1212

13-
<form action="#" hx-ext="htmx-plotly" hx-post="/get-data" hx-swap="none" plot-id="my-plot">
13+
<form action="#" hx-ext="htmx-plotly" hx-post="/get-data" hx-swap="innerHTML" hx-target="#xxx" plot-id="my-plot">
1414
<select name="veggies">
1515
<option value="potatoes">potatoes</option>
1616
<option value="carrots">carrots</option>
17-
<option value="broccoli">broccoli</option>
1817
</select>
1918
<input type="submit" value="Submit"/>
2019
</form>
2120

21+
<div id="xxx"></div>
22+
2223
<div id="my-plot"></div>
2324

2425

plotly_utils.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import json
2+
from markupsafe import Markup, escape
23

3-
def plotlyToRestyle(x):
4+
def plotlyToRestyle(x, htmlStr):
45
"""
56
:param x: a Plotly object with a .to_json() implem, e.g. a Figure
7+
:param htmlStr: HTML string that will be passed to HTMX for swapping
68
:return: data that can be passed to restyle in Plotly.js
79
"""
810
z = x.to_json() # .to_json() is Plotly implem
911
def duplicate(w):
1012
w['x'] = [w['x']] # the .restyle() nested array bs
1113
w['y'] = [w['y']]
1214
return w
13-
return [duplicate(w) for w in json.loads(z)['data']]
15+
obj = {
16+
'restyle_data': [duplicate(w) for w in json.loads(z)['data']],
17+
'markup': str(escape(htmlStr))
18+
}
19+
return obj

server.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11

22
from flask import Flask, request, send_file, make_response
3-
import json
4-
from markupsafe import Markup, escape
5-
import jsonpickle
3+
# import json
4+
# from markupsafe import Markup, escape
5+
# import jsonpickle
66
from plotly_utils import plotlyToRestyle
77

88
from plotly_compound_scatter_test import irisScatter1
@@ -19,11 +19,7 @@ def getData():
1919
# return f'Hello, {escape(name)}!'
2020
x = irisScatter1()
2121

22-
# z = x.to_json() # .to_json() is Plotly implem
23-
# w = json.loads(z)['data'][0] # parse back into dict and extract data
24-
# w['x'] = [w['x']] # the .restyle() nested array bs
25-
# w['y'] = [w['y']]
26-
w = plotlyToRestyle(x)
22+
w = plotlyToRestyle(x, '<b>It wOrKs</b>')
2723
print(f'.restyle data: {w}')
2824
return make_response(w)
2925

0 commit comments

Comments
 (0)