Introduction
Are you a die-hard SE80 ABAP developer, or allergic to JavaScript? Or perhaps you just want to create some
"freestyle" custom Fiori-like apps for your clients or your company without wasting too much time learning new frameworks or relying on some third party?
If so, then this post might be of some interest to you.
Don't get me wrong, I do like JavaScript as a programming language, but I would like to present an alternative to the Single Page Application (SPA) frameworks, like Angular, Vue, React and SAPUI5 that have taken the world by storm over the last decade.
In this blog post, we take a look at the main differences between the SPA model and the model promoted by htmx, and explore a small app to demonstrate its usage.
From SPA to htmx
Single page application (JSON over the wire)
As a reminder, Single Page Applications are applications built to run on a single webpage. After the initial load of one HTML page and some JavaScript, they rely upon Ajax ("Asynchronous Javascript and XML") requests to pass JSON data objects to and from the server to the client to update the HTML page via JavaScript and the Document object model (DOM) API, without the need to reload the entire page.
Single Page Application lifecycle
HTML (fragment) over the wire
In December 2020, David Heinemeier Hansson, creator of Ruby on Rails, CTO and co-founder of Basecamp, got some attention (at least mine) with a blog post proposing a departure from the SPA model.
You can write fast, modern, responsive web applications by generating your HTML on the server, and delivering that (with a little help) directly to the browser. You don’t need JSON as an in-between format. You don’t need client-side MVC frameworks. You don’t need complicated bundling and transpiling pipelines. But you do need to think different. [...]
When we embrace HTML as the format to send across the wire, we liberate ourselves from having to write all the code that creates that HTML in JavaScript. You now get to write it in Ruby or Erlang or Clojure or Smalltalk or any programming language that might set your heart aflutter. We return the web to a place full of diversity in the implementations, and HTML as the lingua franca of describing those applications directly to the browser.
https://m.signalvnoise.com/html-over-the-wire/
This sounds like old SSR (server-side rendering) from the pre-SPA era, but with a twist: after receiving HTML from the server, the browser asynchronously requests fragments of HTML, instead of JSON data, to dynamically alter parts of the page based on user interactions or events occurring on the server. The logic, and the state, remain on the server.
HTML over the wire lifecycle
I later learned that the model behind this "HTML over the wire" concept was not so new and already expressed in other frameworks such as Phoenix LiveView (2018) or Laravel Livewire (2019).
Htmx library
But there’s an even longer-term advocate of this model: Carson Gross, creator of the htmx library.
While htmx is fairly new with its first release in 2020, its predecessor, intercooler.js, dates back to 2013.
Carson is a natural contrarian, as his pinned Twitter post shows, and his company motto too ("We find hot, new industry trends and then do the opposite of that").
To quote the website, htmx allows you to access AJAX, CSS Transitions, WebSockets and Server-Sent Events directly in HTML, using attributes, so you can build modern user interfaces with the simplicity and power of hypertext.
Htmx is small (~10k min.gz'd), dependency-free, extendable & IE11 compatible.
It's also an attempt to complete HTML as a hypertext (as its name implies, htmx is an extension to HTML).
Basic examples
The htmx library is provided as a simple JavaScript file you can just reference from a CDN: no bundling is required.
In this example, when you click on the button, htmx issues a POST request to the url "/clicked" (because of the "hx-post" tag) and swaps the button tag with the (html fragment) responses (because of the "hx-swap" tag), without reloading the full page.
And you can also issue a GET request or swap the innerHTML or any other tag of the current html page if you want.
More examples are provided on the website and I highly recommend that you have a look at them here.
What makes htmx special is its minimalism:
- You don't need any tooling.
- You can adopt htmx incrementally.
- You can do a lot purely in terms of HTML (without any JavaScript).
- If you really want/need some interactivity for your pages without touching the server, you can always use lightweight client frameworks such as:
If you want more information about the concepts behind htmx (REST, HATEOAS), I encourage you to watch this talk from Carson (just mentally replace Django by SAP and Python by ABAP, and all his talk remains relevant, thanks to the "HOWL stack" — Hypertext On Whatever Language).
Fundamental Library Styles
If you want to give your app a Fiori look, don't look any further than the Fundamental Library Styles, a light-weight presentation layer that you can use with any SPA framework and even with plain HTML; a perfect match for htmx.
Another well-suited option for htmx might be Web Components, but I haven't experimented with them yet.
Small demo app
Introduction
To give you a better feel of the potential of a "Hypermedia-Driven Application" on SAP, I created a micro "single class application" in just 195 single lines of ABAP code.
It will illustrate 3 UX patterns made possible by htmx (among many others):
- active search: search as the user enters text,
- out-of-band (oob) content update: update multiple HTML fragments with the same http response,
- and infinite scroll.
Installation
You can choose any IDE to develop such apps, even your tried and tested SE80.
The code of this app/class is available on Github here.
It should work out of the box on any 7.51+ system. This requirement is not related to htmx, but to my use of some ABAP keywords. Actually, htmx should work with any system starting from the 6.10 release of Web Application Server.
Just copy the raw data and paste it to a new ZTF_HANDLER class (after selecting "Source Code-Based" if you are using SE80).
Once the code is activated, head over to SICF to create a new service.
It can be on the root or, like in the example below, under a 'custom' parent service with no handler.
Add your class name to the handler list and save.
And finally, in the SICF tree, right-click on your service and select "Activate Service", then "Test Service".
Your browser should open automatically and display your service for testing.
Note that the search function of the app is really basic, and case sensitive.
Also pay attention to the scrollbar handle on the right when you scroll down the results: it goes up every time a batch of 100 new rows is appended to the table.
Some code explanations
Before diving into the code, we have to keep in mind the structure of the app and its components.
Please also note that this app is bare-bones on purpose: to keep the code clear and simple, and focus on htmx.
The ABAP code is straightforward, so I will only explain the parts involving the htmx library.
if_http_extension~handle_request
Like every ICF service handler, the ZTF_HANDLER class implements the if_http_extension interface and its if_http_extension~handle_request method.
This method acts as the "controller" part of the app.
First we filter the HTTP method to only keep the GET requests, and then we get the current path of the app and the searched string, if any.
Then we check if the HTTP request is issued by the browser following normal navigation or from the htmx library (the 'HX-Request' header field is always populated with the value "true" for every htmx-initiated XMLHttpRequest).
- In the first case (initial load of the application), all the fragments making up the whole page are returned to the browser.
- In the second case, only the fragments required by the "app-action" are returned.
Each HTML fragment is generated by its own method (prefixed with 'html_').
Note that the "app-action" is a custom header field and not provided by the htmx library; its name is arbitrary and we will see later where this header field is defined.
Each "app-action" (think of it as an "OK code") triggers a different response from the server.
- search
- The whole table is sent to the browser, with the first batch of rows (100 rows or less).
- search_init
- Same as "search" but the searched_string is cleared and another HTML fragment is sent "out of band" with the response, the search bar itself, to clear its content. This "oob" feature allows you to update not only one, but as many HTML fragments of the original page within the same response as you want.
- scroll
- When the user scrolls the list, a request is automatically sent to the server to get the next batch of rows (we will see how later).
html_page
This app only has three dependencies:
- the htmx.js file for the core htmx library,
- the fundamental-style.css file for the Fundamental Library Styles,
- the fonts used by the Fundamental Library Styles (see here for more info).
Here we link to a CDN but you should probably store these libraries in the standard SAP MIME Repository or using the ICM or on any other internal HTTP server.
We also added a "htmx-config" meta tag to configure the default htmx swap mode with "outerHTML", to swap the whole fragment and not only its content (as with the default "innerHTML" mode).
html_searchbar
To get started, all htmx attributes begin with "hx-" or "data-hx-". The two forms are equivalent but the latter doesn't issue errors when you check your generated HTML code with some tools like this one.
Here we have two reactive elements:
- an input element for the search string,
- a button to refresh the search bar and the list.
Here are some basic explanations about the various htmx attributes used here.
- data-hx-push-url
- Note that this attribute, like the data-hx-target attribute, is inherited by the child tags; so, instead of repeating this attribute on the input and button tags, we can just place it on the parent div tag.
- This tag is used to push the URL into the location bar; each time we search a new table name, it creates a new entry history, so that we can navigate back to our previous searches.
- data-hx-target
- This attribute allows us to target a different HTML element for swapping than the one issuing the request.
- The target can be any valid CSS query selector (and more).
- data-hx-trigger
- This attribute allows us to specify what triggers the request.
- As we want an "active search", the request will be triggered 250ms after the user stops typing (or if he leaves the search field).
- data-hx-get
- This attribute causes an element to issue a GET request to the specified URL.
- data-hx-headers
- This attribute allows us to add to the headers that will be submitted with the request. Its value is in JSON format.
- "app-action" is the arbitrary header field name I chose to send to the if_http_extension~handle_request.
- data-hx-swap-oob (in the first <div>)
- This attribute allows you to specify that some content in a response should be swapped into the DOM somewhere other than the target, that is "Out of Band". Thanks to this attribute, when the user initialises their search, the server response updates both the html table and the search bar. Of course, this attribute is not inherited.
You can find more information about these attributes and the other htmx attributes here.
html_table_rows
Here, we not only display the batch of table rows but also an invisible row whose purpose is to trigger the load of the next batch of rows when it is scrolled into the viewport (or "revealed" as defined in the "data-hx-trigger" attribute).
The "data-hx-vals" attribute allows us to add some parameters to those that will be submitted with the request. We have to use this attribute to get the next batch of rows with the same search string as the original request.
The "data-hx-target" and the "data-hx-swap" attributes are defined so as to add the new batch of rows to the end of the table.
Advice from my own experience
Use htmx
You can find some libraries comparable to htmx (I first tried unpoly) but the minimalism of htmx pays off in the end.
Embrace HTML and CSS
When I first experimented with htmx, my initial idea was to create an ABAP library to encapsulate HTML, htmx and the Fundamental Library Styles, but it was tedious and without any real added value.
HTML and CSS are two of the core technologies for building webpages: you will not waste your time adding those skills to your toolbox.
You can head over to the
Fundamental Library Styles website to get started. Just select a component and click on "Show code" to get the required HTML and CSS.
Learn how the SAP Internet Communication Framework works
You can design your own app architecture in many different ways and with many different ABAP technologies (BSP, ABAP REST Library...) but using the ICF fully and writing your own HTTP handlers is a really good option.
You can have multiple HTTP handler classes handling the same request and split your app in a tree of ICF services where each service corresponds to a component, or a group of components, which completes the main server response, quite similarly to "nested routes" in frameworks such as Remix or in React Router v6.
Also, don't forget
ICF logging.
Simplify your state management
If you are on HANA, store everything (including all user inputs) in the database; if you have to call BAPIs, store intermediate data in custom tables (just like SAPUI5 Draft Handling). You can even store each field update from the user.
Stay humble
When you think there is an issue with the htmx library, check your code instead (and the htmx documentation or discord).
Conclusion
After a few weeks of using htmx, here are the main advantages and disadvantages of this library, from my point of view.
Pros
- Full ABAP with direct access to existing BAPI, FM, classes, CDS views...
- Easy to grasp and well-documented libraries (both htmx and Fundamental Library Styles)
- Fast iterative process
- No build process, you just have to activate your ABAP code.
- When building and testing HTML fragments, you don't even have to reload your HTML page.
- No mockup data
- You have your full DB data available at your fingertips.
- Simplified state management
- No API / Frontend desynchronization
- No duplication of logic
- Code editor of choice
- I personally use VSCodium on Linux without a glitch, thanks to murbani and lars.hvam,
- but ADT and SE80 are fine.
- Easier debugging
- All your code is in one place and you have no dependency on external code.
- Architecture freedom
- on the server-side
- on the frontend: I used Fundamental Library Styles but you can use any CSS framework if you want.
- Performance
- It could be counter-intuitive because of all these round trips between the browser and the SAP server, but the fact is htmx is damn fast (faster than Fiori apps in our S/4HANA environment).
- Security
- The htmx library itself is less than 2,500 lines of easily auditable JavaScript code and, with it, you avoid using any other JavaScript (npm) module, usually overwhelmingly found in current SPA frontend developments or toolings.
- Ubiquity
Cons
- Deviation from the web development model promoted by SAP
- Not the best solution for fast prototyping
- But as a programmer once said: "In the beginning you always want results. In the end all you want is control".
- No ecosystem of reusable components (yet!)
I would really enjoy seeing some of you try this htmx library so that we can share our experiments and best practices (and soon, "hmtx" might even become a new SAP community tag!).
(This blog post is a tribute to brian.mckellar and thomas.jung whose book "Advanced BSP Programming" had been gathering dust on one of my shelves for too many years).