In this post, we will show you how to include javascript libraries like echarts into your htmx workflow using htmx extensions.
¶
Prerequisites
Basic knowledge of pyecharts & htmx - If you haven't already, please
[
check out this post
](https://www.advantch.com/blog/how-to-add-charts-to-your-python-application/?utm_campaign=blog)
about how to set up pyecharts for your Django/Python application.
The code for this tutorial
[
can be found here
](https://replit.com/@advantch/advantch-django-htmx-echarts)
¶
Concepts Covered
We want to make our filters to the chart so we can filter on dates.
*
htmx extensions
*
- we will create an htmx extension to handle parsing data from the backend and updating the chart.
*
triggering client side
*
actions from the backend
*
hyperscript
*
- a small example of how to use the hyperscript library with htmx
¶
Chart View
We will create a view to handle htmx requests. The view will use parameters provided in the request to return data for the correct period and chart type.
text
`def chart_view_hx(request):
"""Returns chart options for echarts"""
period = request.GET.get("period", "week")
chart_id = request.GET.get("chart_id")
chart_type = request.GET.get("chart_type")
days_in_period = {
"week": 7,
"month": 30,
}
filter_by = days_in_period.get(period,"week")
# simulate fetching this from your database
chart_title = CHART_TYPES.get(chart_type, "page_views")
chart_data = fake_chart_data(filter_by, chart_title)
# render the chart and update options to include id
chart = line_chart(chart_data)
chart.options["id"] = chart_id
chart._prepare_render()
data = chart.json_contents
response = HttpResponse(content=chart.json_contents)
# optional
# if using the django_htmx library you can attach any clientside
# events here . For example
# trigger_client_event(response, 'filterchanged', params={})
return response`
Notes
period = request.GET(..)
- fetch the period, chart_id, chart_type from the request
chart_data = fake_chart_data()
- this simulates fetching data from an api or your database.
chart = line_chart(chart_data)
- call the method that build the chart. This method will return an instance of a Line chart with all the options we need to update the chart
chart.options["id"]
- here we add the id of the chart we want to update and then call the prepare_render method to ensure that the data is converted to json.
trigger_client_event
- If you need to piggyback additional events to be triggered on the client-side, you can attach them here.
If you are using Django, the django_htmx library provides the trigger_client_event method for this
You can also do this for yourself by attaching the HX-Trigger to the response. See the
[
htmx docs
](https://htmx.org/headers/hx-trigger/)
for more details on this.
¶
Update the urls
*
django_project/urls.py
*
text
`urlpatterns = [
...
path('charts', chart_view_hx, name='charts-hx')
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)`
Notes
path('charts', ..)
- add the view to urls.py.
static(settings.STATIC_URL..)
- You will notice we have also added the Django
static
helper function here so we can staticfiles in debug mode.
¶
Add htmx extension
Htmx provides an extension mechanism for defining and using extensions within htmx-based applications.
js
`htmx.defineExtension('echarts', {
transformResponse: function (text, xhr, elt) {
// parse json data
var data = JSON.parse(text);
// fetch echart element
var option = data;
var chartContainer = document.getElementById(data.id);
var chart = echarts.getInstanceByDom(chartContainer);
// clean up options and update chart
delete data.id;
chart.setOption(option);
}
});
`
Notes
htmx.defineExtension('echarts', {}
- define an extension called echarts. This will be referenced on the element by setting the
hx-ext
attribute i.e.
<div hx-ext='echarts'>
var data
= JSON(parse(text)) - parse the chart data from the backend
var chartContainer
= document.getElementById(data.id); - fetch the chartContainer element from the DOM using the id provided in our data
delete data.id
- we clean up our options object to remove the id. We only needed the Id so we could identify the chart to update.
chart.setOption(option)
- finally, use the
setOption()
method from the echarts library to update the data on the chart.
¶
Update the templates
Next, we need to update our chart template to include the filter buttons.
We will create a new filter_button component for the filters.
text
` <button
hx-get="{% url 'charts-hx' %}?chart_id={{ c.chart_id }}&chart_type=page_views&period={{period}}"
hx-swap="none"
id="id_last_week_filter_btn"
class="bg-white rounded shadow p-2 text-sm flex items-center font-semibold mr-2 cursor-pointer
{% if selected %}text-indigo-600 {% else %}text-gray-600 {% endif %}">{{label}}
</button>
`
Notes
hx-get="{% url 'charts-hx' %}
… - include the url and required parameters
hx-swap ='none'
- set this to none, the response from the backend will not be swapped out.
{{chart_type}},{{chart_id}},{{period}}
- the component expects these variables to be available in the context
{% if selected .. %}
- here we are adding a bit of styling to highlight the selected button.
The main template will remain largely unchanged from the previous tutorial. We have modified it to include the filter_buttons. To keep things simple, we have removed the additional charts.
text
`
<head>
...
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<script src="{% static 'js/ext/hx-echarts.js'%}"></script>
...
</head>
<div class='flex' hx-ext="echarts">
{% include 'components/chart_filter.html' with period='week' c=charts.0 label='Last Week' selected=True %}
{% include 'components/chart_filter.html' with period='month' c=charts.0 label='Last Month' %}
</div>
`
Notes
<head >..</head>
- the head section has been modified to include the htmx library, the echarts extension script.
{% include 'components/chart_filter.html'
- include the components in the main template and define the variables for the chart, label, the period to be used in the filter as well as the
¶
UI Improvements
We can improve it by letting our users know which tab is active. This can be achieved using vanilla javascript or alpinejs. In this example, we will use hyperscript, a companion library for htmx to achieve this.
text
`<head>
...
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.5"></script>
<script src="{% static 'js/ext/hx-echarts.js'%}"></script>
...
</head>
<div class='flex' hx-ext="echarts"
_="on htmx:afterOnLoad take .text-indigo-600 for event.target"
>
{% include 'components/chart_filter.html' with period='week' c=charts.0 label='Last Week' selected=True %}
{% include 'components/chart_filter.html' with period='month' c=charts.0 label='Last Month' %}
</div>`
Notes
<head></head>
- we have updated the head section to include hyperscript.
_="on htmx:afterOnLoad take
…” after the response is received, the
text-indigo-600
class will be moved to the clicked button.
¶
Conclusion
In this post, we learned how to use htmx extensions with the echarts library. The htmx library provides a convenient mechanism to organize your codebase and integrate other libraries into your htmx workflow.
¶
Further reading
