Fiori-like web app development in pure ABAP with htmx and Fundamental
If so, then this post might be of some interest to you.
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)
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. […]
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.
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).
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).
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.
- If you really want/need some interactivity for your pages without touching the server, you can always use lightweight client frameworks such as:
- hyperscript (Carson’s own library) – look at this article for a short introduction,
- or VanillaJS.
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
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.
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.
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.
- The whole table is sent to the browser, with the first batch of rows (100 rows or less).
- 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.
- 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).
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).
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.
- 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.
- 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).
- 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).
- This attribute causes an element to issue a GET request to the specified URL.
- 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.
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
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.
When you think there is an issue with the htmx library, check your code instead (and the htmx documentation or discord).
After a few weeks of using htmx, here are the main advantages and disadvantages of this library, from my point of view.
- 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 Marcello Urbani 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.
- 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).
- 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).
Great Blog !!! Honestly I never even heard of htmx, but I would definitely take a look into it when I have more spare time. Because I'm really interested on using Fiori-Like Development as close as possible to ABAP specially for SPA without all the UI5 boilerplate.
I have developed BSPs in the past (very long past) and I did a bit of UI5 development but I didn't like it at all.
Thanks for sharing
Thank you Rodrigo! I hope you will find enough time to try out htmx soon. I'm looking forward to sharing our findings.
Wow, this is such an interesting blog! I got very intrigued by htmx after watching this interesting YouTube interview with Carson, the main designer of htmx. Thank you for explaining how htmx can be used in a ABAP world!
Thank you Hugo! I also watched that video when it was published. We often have to be exposed to a new concept multiple times, in different contexts, to really take it into consideration.
You're so right Patrick, new concepts take a (long) while to sink in. htmx is a new concept that makes me excited about web development for the very first time!
Very interesting topic and structured blog. It kind of reminds me of HTMLB, but I'm sure the length of time is fooling me, isn't it?
Would love to see a blog on how you set up your editor of choice.
You can follow this one. I have just added the Gruvbox Theme and the MonoLisa font with "editor.fontLigatures": true.
Interesting idea. I personally like SPAs better (and I suspect the slow performances have more to do with UI5 than js rendering in general), but it's a matter of taste.
What I really don't like is generating html by string concatenation, seems pretty hard to maintain. Maybe something like Mustache would work better for me.
Anyway looks like a great alternative!
Thank you for your nice comment Marcello, and for your awesome ABAP remote plugin! You can of course use any templating engine you like, even for Mustache templates.
This blog's worth more than one like: mentioning ICF Recorder (man, that save lives) and htmx itself... that was something I thought it would never be talked on SAPverse.
About the reusable components, that would be UI5-Web Components?
Your comment is worth more than one “thank you”, Thales! I mentioned UI5 Web Components in my post because I have high hopes for this library, but I have yet to check if they pair well with htmx.
Interesting topic! The blog is very easy to understand even for me who never heard about htmx.
I am already creating a demo app based on your code, while testing other htmx functions like Edit Row and Click to Edit.
One question, how did you know that you need to add "data-" before each attributes?
In the htmx library, they are "hx-get" and "hx-trigger" but it did not work for my code and it did not trigger a GET method. Once I followed your code and changed to "data-hx-get", it worked.
Thank you Aocheng! Happy to see someone trying this library.
"data-hx-get" and "hx-get" are really the same thing (see end of the htmx introduction). Did you change something else?
Thank you for clarifying, I think it was a mistake on my side. It triggered Get method with or without "data-" prefix.
I might post my own blog on this topic after I tested CRUD operations!
Hi Patrick VILLENEUVE, today my demo app stopped pulling styles from Fundamental Style CSS. It was working a month ago right after I tried it out with your demo. Now my app looks miserable..
Does this happen to your demo app too?
Yes, thank you for the warning.
As a quick fix, you can pin the previous 0.22.0 Fundamental Styles release with: https://email@example.com/dist/fundamental-styles.css.
I have updated the Github repo with the latest Fundamental Styles release.
This is an awesome contribution to the community. This shows the way to regular ABAPers who are being challenged to produce better UX with minimal learning .
Great work and Thanks 🙂
Thank you so much Murali! I share your enthusiasm for this library.