Skip to content

xmlet/HtmlFlow-Datastar-Examples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HtmlFlow-Datastar-Examples

This repository contains two projects. The first gathers the Datastar examples, providing a practical showcase of how the DSL can be used in real backend-driven web applications. The second implements an equivalent version of the classic Spring Petclinic by replacing the Thymeleaf template engine with the Type-Safe Hypermedia-First DSL for Reactive Backend-Driven Web Applications.

Datastar Examples

🚀 DataStar version: 1.0.1

This project includes a demo web application featuring examples from Data-Star, running on Ktor and http4k and using the HtmlFlow Kotlin DSL to generate HTML.

HtmlFlow DSL provides type-safe backend handlers for DataStar actions. In the following samples, note how the action get is attached to a handler given by a function reference, this function being annotated with Path, from Jakarta, where the resource location for the request is specified.

HtmlFlow DSL also provides a type-safe way to define Datastar attributes in Kotlin. Next is a sample of the Counter example with a strongly typed signal and another one from Active Search using events and modifiers:

div {
  val count: Signal = dataSignal("count", 0)
  div {
    dataInit { get(::getCounterEvents) }
    span {
      attrId("counter")
      dataText{ +count }
    }
  }
}
div {
  attrId("demo")
  input {
    attrType(EnumTypeInputType.TEXT)
    attrPlaceholder("Search...")
    dataBind("search")
    dataOn(Input) {
      get(::search)
      modifiers { debounce(200.milliseconds) }
    }
  }
}
Note that the count variable is of type Signal and simply binds to the data-text in a type-safe way, regardless of the name passed to the Signal constructor. Modifiers such as debounce are added inside a lambda using builders, following an idiomatic Kotlin style.

Also, HtmlFlow DSL supports strongly typed DataStar expressions, allowing their composition with the infix operator and, such as in the expression !fetching and get(::clickToLoadMore). Note how the JavaScript expression for the onclick event handler (right side) is expressed in Kotlin through HtmlFlow in a type-safe way:

button {
  val fetching = dataIndicator("_fetching")
  dataAttr("disabled") { +fetching }
  dataOn(Click) {
      !fetching and get(::clickToLoadMore)
  }
  text("Load More")
}
<button
  data-indicator:_fetching
  data-attr:aria-disabled="$_fetching"
  data-on:click="!$_fetching && @get('/examples/click_to_load/more')"
>
    Load More
</button>

Change to the datastar-examples directory and run the application with Gradle. The application will start two servers, one for Ktor and another for http4k, each running the same examples.

cd ./datastar-examples

Run with:

./gradlew run

Then open http://localhost:8080 for the ktor server and http://localhost:8070 for http4k in your browser.

Check all examples from the index page and corresponding HtmlFlow view definitions:

Spring Petclinic with HtmlFlow and DataStar

The petclinic-htmlflow module contains an implementation of the Spring Petclinic application using HtmlFlow Kotlin views and DataStar hypermedia controls; it replaces the previous Thymeleaf templates. The goal is to preserve the original Petclinic domain and features while exploring a backend-driven, hypermedia-first UI model that is:

  • Type-safe: views are expressed in Kotlin using the HtmlFlow DSL.
  • Server-driven: DataStar actions and signals enable server-initiated UI updates.
  • Incremental and efficient: updates can patch page fragments (for example table rows) instead of reloading whole pages.

Run the Petclinic application with Gradle:

cd ./petclinic-htmlflow
./gradlew bootRun

Open http://localhost:8080 in your browser. The application supports the usual Petclinic features (owners, pets, visits) implemented with HtmlFlow/DataStar rather than Thymeleaf.

Thymeleaf vs HtmlFlow + DataStar

Below is a simplified comparison showing the traditional Thymeleaf findOwners form and an equivalent HtmlFlow view that uses data binding and a Debounce modifier to drive the search results from the server.

Thymeleaf (classic form)

<h2>Find Owners</h2>
<form th:object="${owner}" th:action="@{/owners}" method="get">
  <div id="lastNameGroup">
    <label>Last name</label>
    <div>
      <input th:field="*{lastName}" size="30" maxlength="80"/>
      <span class="help-inline">
        <div th:if="${#fields.hasAnyErrors()}">
          <p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
        </div>
      </span>
    </div>
  </div>
  <button type="submit">Find Owner</button>
  <a th:href="@{/owners/new}">Add Owner</a>
</form>

HtmlFlow + DataStar (server-driven)

h2 { text("Find Owners") }
val lastName = dataSignal("lastName")
dataText { +" Searching for users with last name $lastName " }
input {
  dataBind(lastName)
  dataOn(Input) {
    get(::searchhOwners)
    modifiers { debounce(200.milliseconds) }
  }
}
table {
  tableHead()
  tbody { tableBody() }
}
a { attrHref("/owners/new"); text("Add Owner") }

Why use this approach?

  • Server-driven UIs keep view code on the backend, reducing the need for a separate frontend codebase while still delivering interactive behavior.
  • Fine-grained patches (signals/events) reduce network traffic compared with full page reloads.
  • Kotlin + HtmlFlow provide compile-time guarantees for view changes and make refactors safer.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors