All Projects → xmlet → HtmlFlow

xmlet / HtmlFlow

Licence: MIT license
HtmlFlow Java DSL to write typesafe HTML

Programming Languages

java
68154 projects - #9 most used programming language
HTML
75241 projects

Projects that are alternatives of or similar to HtmlFlow

Jet
Type safe SQL builder with code generation and automatic query result data mapping
Stars: ✭ 373 (+213.45%)
Mutual labels:  typesafe
Types First Ui
An opinionated framework for building long-lived, maintainable UI codebases
Stars: ✭ 114 (-4.2%)
Mutual labels:  typesafe
Vuex Class Component
A Type Safe Vuex Module or Store Using ES6 Classes and ES7 Decorators written in TypeScript.
Stars: ✭ 189 (+58.82%)
Mutual labels:  typesafe
Strictyaml
Type-safe YAML parser and validator.
Stars: ✭ 836 (+602.52%)
Mutual labels:  typesafe
Purescript Graphql
End to End typesafe GraphQL with PureScript
Stars: ✭ 79 (-33.61%)
Mutual labels:  typesafe
Safe Units
Type-safe TypeScript units of measure 👷📏
Stars: ✭ 137 (+15.13%)
Mutual labels:  typesafe
event-bus
Typesafe cross-platform pubsub event bus ensuring reliable communication between fragments and micro frontends.
Stars: ✭ 15 (-87.39%)
Mutual labels:  typesafe
Thread
type safe multi-threading made easier
Stars: ✭ 34 (-71.43%)
Mutual labels:  typesafe
Undux
⚡️ Dead simple state for React. Now with Hooks support.
Stars: ✭ 1,488 (+1150.42%)
Mutual labels:  typesafe
Chaos
The Chaos Programming Language
Stars: ✭ 171 (+43.7%)
Mutual labels:  typesafe
Hoodie
Hoodie is a type safe wrapper around jersey http client
Stars: ✭ 22 (-81.51%)
Mutual labels:  typesafe
Units Of Measure
Type-safe dimensional analysis and unit conversion in Kotlin.
Stars: ✭ 69 (-42.02%)
Mutual labels:  typesafe
Modern Wasm Starter
🛸 Run C++ code on web and create blazingly fast websites! A starter template to easily create WebAssembly packages using type-safe C++ bindings with automatic TypeScript declarations.
Stars: ✭ 140 (+17.65%)
Mutual labels:  typesafe
Go Queryset
100% type-safe ORM for Go (Golang) with code generation and MySQL, PostgreSQL, Sqlite3, SQL Server support. GORM under the hood.
Stars: ✭ 599 (+403.36%)
Mutual labels:  typesafe
Flowkit
A declarative type-safe framework for building fast and flexible list with Tables & Collection
Stars: ✭ 215 (+80.67%)
Mutual labels:  typesafe
trembita
Model complex data transformation pipelines easily
Stars: ✭ 44 (-63.03%)
Mutual labels:  typesafe
Http4k
The Functional toolkit for Kotlin HTTP applications. http4k provides a simple and uniform way to serve, consume, and test HTTP services.
Stars: ✭ 1,883 (+1482.35%)
Mutual labels:  typesafe
SwiftyPods
Type safe pods
Stars: ✭ 15 (-87.39%)
Mutual labels:  typesafe
flutter firestore ref
Cross-platform(including web) Firestore type-safe wrapper.
Stars: ✭ 72 (-39.5%)
Mutual labels:  typesafe
Typedapi
Build your web API on the type level.
Stars: ✭ 165 (+38.66%)
Mutual labels:  typesafe

HtmlFlow

Build Status Maven Central Version Coverage Status Petclinic Sample

HtmlFlow is a Java DSL to write typesafe HTML documents in a fluent style. Use one of its view() factory methods to get started with HtmlFlow, such as the following sample, which produces the HTML on the right side. Use the utility Flowifier.toFlow(url) to get the HtmlFlow definition from the corresponding HTML source:

view.render()  ↴
HtmlView view = StaticHtml
  .view()
    .html()
      .head()
        .title().text("HtmlFlow").__()
      .__() //head
      .body()
        .div().attrClass("container")
          .h1().text("My first page with HtmlFlow").__()
          .img().attrSrc("http://bit.ly/2MoHwrU").__()
          .p().text("Typesafe is awesome! :-)").__()
        .__() //div
      .__() //body
    .__(); //html
<html>
    <head>
        <title>HtmlFlow</title>
    </head>
    <body>
        <div class="container">
            <h1>My first page with HtmlFlow</h1>
            <img src="http://bit.ly/2MoHwrU">
            <p>Typesafe is awesome! :-)</p>
        </div>
    </body>
</html>
↸  Flowifier.toFlow(url)

Finally HtmlFlow is the most performant engine among state of the art template engines like Velocity, Thymleaf, Mustache, etc and other DSL libraries for HTML such as j2Html and KotlinX Html. Check out the performance results in the most popular benchmarks at spring-comparing-template-engines and our fork of xmlet/template-benchmark.

Check the implementation of the sample Spring-based petclinic with HtmlFlow views xmlet/spring-petclinic. You can find different kinds of dynamic views there.

Why another templating engine ?

Every general purpose language has its own template engine. Java has several. Most of the time, template engines have templates that are defined in new external DSL. To allow them to produce a view based on the templates files, they generally use the concept of model.

One of the problems of this technic is that you will end up with a template that won't be type checked. So if you have a typo inside your template, the compiler won't be able to help you before the template is rendered.

HtmlFlow took a different approach. Templates are expressed in an internal DSL. You will write normal Java code to produce your template. So the full Java tool chain is at your disposal for templating. Put it simply, HtmlFlow templates are essentially plain Java functions.

HtmlFlow is not the only one using this approach. But it's the fastest one. Bonus points it also produces only valid HTML document according to HTML 5.2.

Table of Contents

Installation

First, in order to include it to your Maven project, simply add this dependency:

<dependency>
    <groupId>com.github.xmlet</groupId>
    <artifactId>htmlflow</artifactId>
    <version>3.9</version>
</dependency>

You can also download the artifact directly from Maven Central Repository

Getting started

All methods (such as body(), div(), p(), etc) return the created element, except text() which returns its parent element (e.g. .h1().text("...") returns the H1 parent object). The same applies to attribute methods - attr<attribute name>() - that also return their parent (e.g. .img().attrSrc("...") returns the Img parent object).

There is also a special method __() which returns the parent element. This method is responsible for emitting the end tag of an element.

The HTML resulting from HtmlFlow respects all HTML 5.2 rules (e.g. h1().div() gives a compilation error because it goes against the content allowed by h1 according to HTML5.2). So, whenever you type . after an element the intelissense will just suggest the set of allowed elements and attributes.

The HtmlFlow API is according to HTML5.2 and is generated with the support of an automated framework (xmlet) based on an XSD definition of the HTML5.2 syntax.

Thus, all attributes are strongly typed with enumerated types which restrict the set of accepted values. Finally, HtmlFlow also supports dynamic views with data binders that enable the same HTML view to be bound with different object models.

Output approaches

Consider the definition of the following view that is late rendered by the function passed to the view() method:

static HtmlView view = StaticHtml.view(v -> v
            .html()
                .body()
                    .p().text("Typesafe is awesome! :-)").__()
                .__() //body
            .__()); // html

Thus you can get the resulting HTML in three different ways:

  1. get the resulting String through its render() method or
  2. directly write to any Printstream such as System.out or
  3. any other PrintStream chain such as new PrintStream(new FileOutputStream(path)).

NOTE: PrintStream is not buffered, so you may need to interleave a BufferedOutputStream object to improve performance. On the other hand render() internally uses a StringBuilder which shows better speedup.

String html = view.render();        // 1) get a string with the HTML
view
    .setPrintStream(System.out)
    .write();                       // 2) print to the standard output
view
    .setPrintStream(new PrintStream(new FileOutputStream("details.html")))
    .write();                       // 3) write to details.html file

Desktop.getDesktop().browse(URI.create("details.html"));

Regardless the output approach you will get the same formatted HTML document:

<!DOCTYPE html>
<html>
    <body>
        <p>
            Typesafe is awesome! :-)
        </p>
    </body>
</html>

Dynamic Views

A dynamic view is based on a template function BiConsumer<DynamicHtml<U>, U>, i.e. a void function that receives a dynamic view (DynamicHtml<U>) and a domain object of type U -- (DynamicHtml<U>, U) => void. Given the template function we can build a dynamic view through DynamicHtml.view(templateFunction).

Next we present an example of a view with a template (e.g. taskDetailsTemplate) that will be later bound to a domain object Task. Note the use of the method dynamic() inside the taskDetailsTemplate whenever we are binding properties from the domain object Task. This is a mandatory issue to enable dynamic bind of properties, otherwise those values are cached and the domain object Task will be ignored on further renders.

HtmlView<Task> view = DynamicHtml.view(HtmlLists::taskDetailsTemplate);

static void taskDetailsTemplate(DynamicHtml<Task> view, Task task) {
    view
        .html()
            .head()
                .title().text("Task Details").__()
            .__() //head
            .body()
                .dynamic(body -> body.text("Title:").text(task.getTitle()))
                .br().__()
                .dynamic(body -> body.text("Description:").text(task.getDescription()))
                .br().__()
                .dynamic(body -> body.text("Priority:").text(task.getPriority()))
            .__() //body
        .__(); // html
}

Next we present an example binding this same view with different domain objects, producing different HTML documents.

List<Task> tasks = Arrays.asList(
    new Task(3, "ISEL MPD project", "A Java library for serializing objects in HTML.", Priority.High),
    new Task(4, "Special dinner", "Moonlight dinner!", Priority.Normal),
    new Task(5, "US Open Final 2018", "Juan Martin del Potro VS  Novak Djokovic", Priority.High)
);
for (Task task: tasks) {
    Path path = Paths.get("task" + task.getId() + ".html");
    Files.write(path, view.render(task).getBytes());
    Desktop.getDesktop().browse(path.toUri());
}

Finally, an example of a dynamic HTML table binding to a list of tasks:

static void tasksTableTemplate(DynamicHtml<Stream<Task>> view, Stream<Task> tasks) {
    view
        .html()
            .head()
                .title()
                    .text("Tasks Table")
                .__()
            .__()
            .body()
                .table()
                    .attrClass("table")
                    .tr()
                        .th().text("Title").__()
                        .th().text("Description").__()
                        .th().text("Priority").__()
                    .__()
                    .tbody()
                        .dynamic(tbody ->
                            tasks.forEach(task -> tbody
                                .tr()
                                    .td().dynamic(td -> td.text(task.getTitle())).__()
                                    .td().dynamic(td -> td.text(task.getDescription())).__()
                                    .td().dynamic(td -> td.text(task.getPriority().toString())).__()
                                .__() // tr
                            ) // forEach
                        ) // dynamic
                    .__() // tbody
                .__() // table
            .__() // body
        .__(); // html
}

static HtmlView<Stream<Task>> tasksTableView = DynamicHtml.view(HtmlForReadme::tasksTableTemplate);

public class App {
    public static void main(String [] args)  throws IOException {
        Stream<Task> tasks = Stream.of(
            new Task(3, "ISEL MPD project", "A Java library for serializing objects in HTML.", Priority.High),
            new Task(4, "Special dinner", "Moonlight dinner!", Priority.Normal),
            new Task(5, "US Open Final 2018", "Juan Martin del Potro VS  Novak Djokovic", Priority.High)
        );

        Path path = Paths.get("tasksTable.html");
        Files.write(path, tasksTableView.render(tasks).getBytes());
        Desktop.getDesktop().browse(path.toUri());
    }
}

References

License

MIT

About

HtmlFlow was created by Miguel Gamboa (aka fmcarvalho), an assistant professor of Computer Science and Engineering of ISEL, Polytechnic Institute of Lisbon.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].