htmdart 0.6.0 copy "htmdart: ^0.6.0" to clipboard
htmdart: ^0.6.0 copied to clipboard

Build simple systems with Dart + HTMX

htmdart

Htmdart combines the power of shelf, htmx, sometimes hyperscript and htmleez to give you the simplest and fastest way to build web applications in pure Dart (with a great DX too).

Before Starting #

  • Make sure you're familiar with shelf as Htmdart is built on top of it

    shelf_router is not necessary, htmdart has its own router

  • Make sure you're familiar with hypermedia and htmx
  • Check out htmleez in order to understand how you can compose and render your HTML directly in dart
  • And make sure to read this essay in order to have a better idea of when to use htmx

P.S. Check out all the very informative memes about htmx and hypermedia in here

In case you'd like to add interactivity to your app you can check out:

Install #

Add to your pubspec.yaml:

dart pub add htmdart

Hello World Example #

import 'dart:math';
import 'package:htmdart/htmdart.dart'; /// htmleez is automatically imported with htmdart
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;

final router = Router()
  ..get("/", homePage)
  ..get("/random", randomNumber);

Future<void> main() async {
  final server = await io.serve(router.call, 'localhost', 8080);
  print('Listening on http://${server.address.host}:${server.port}');
}

Response homePage(Request _) {
  return html([
    body([
      div([$id("number"), "0".t]),
      button([
        $hx.get("/random"),
        "Get Random Number".t,
      ]),
    ])
  ]).response;
}

Response randomNumber(Request _) {
  final n = Random().nextInt(100);
  return div([
    $id("number"),
    $hx.swapOob.yes,
    n.toString().t,
  ]).response;
}

Core Concepts #

1. HTML Responses #

  • HtmlResponse
    Wraps a list of HTML components into a shelf.Response with Content-Type: text/html; charset=utf-8.
    Response myPage(Request req) {
      return HtmlResponse([
        html([
          h1(["Hello, world!".t]),
        ]),
      ]);
    }
    
    //You can also use the extensions
    Response myPage(Request req) {
      return html([
        h1(["Hello, world!".t])
      ]).response;
    }
    
  • Extensions
    • On a single HTML: myElement.response
    • On a List<HTML>: myListOfElements.response

2. HTMX utilities #

The hx class provides all the standard HTMX attributes

  • Simple verbs
    hx.get("/path")
    hx.post("/submit")
    hx.put(...)
    hx.delete(...)
    
  • Dynamic handler binding
    // Let's imagine this handler
    Response handleNoteDetails(Request req, String noteId) {...}
    
    final router = Router()..get("/notes/<noteId>",  handleNoteDetails);
    
    // Then in your elements you can automatically pick up the HTTP verb and route
    $hx.handle(handleNoteDetails)
    
    // In case your route has path parameters (like in this case)
    // you can pass them in order as a List<String>
    $hx.handler(handleNoteDetails, [note.id])
    
    // You can also pass query parameters
    $hx.handler(handleNoteDetails, [note.id], {"fromQP": true})
    
    // It renders to
    // hx-get="/notes/myNoteId?fromQP=true";
    
  • Swap controls
    $hx.swap.innerHTML
    $hx.swapOob.yes      // out-of-band swaps
    
  • Extras
    $hx.vals("js:{ count: … }")      // inject JSON values
    $hx.select(".result")            // response selector
    $hx.confirm("Are you sure?")
    
    All the available hx attributes from htmx have been added, you can see them here

3. Hyperscript attribute #

Built-in support for hyperscript's attribute

div([
  className("btn"),
  $_("on click add .active to me then wait 500ms then remove .active"),
  "Click me".t,
])

Renders ->

<div class="btn" _="on click add .active to me then wait 500ms then remove .active">Click me</div>

4. Request Extensions #

On any shelf.Request you can check HTMX metadata:

if (request.isHx)         // true if HX-Request header == "true"
if (request.hxTarget != null)
print(request.hxTriggerName);

Routing #

Use the built-in Router (from htmdart/src/router/router.dart) to declare routes:

final router = Router()
  ..notFoundHandler((req) => html([h1(["404".t])]).response)
  ..get("/", homePageHandler)
  ..post("/increment", incrementHandler);

void main() async {
  final server = await io.serve(router.call, 'localhost', 8080);
  print("Listening on ${server.port}");
}
  • Path parameters
    Response handler(Request req, String param) {...}
    
    final router = Router()..get("/handler/<param>", handler);
    
  • Any Method
    Response handler(Request req, String param) {
      switch(req.method) {
        case "GET": ...
        case "POST": ...
      }
    }
    
    final router = Router()..any("/any", handler);
    
  • Grouping
    final apiGroup = router.group("/api");
    apiGroup.get("/items", listItems);
    apiGroup.post("/items", createItem);
    
  • Static files
    router.static("/public", "web/public");
    
  • Middleware
    • Built-in support for middlewares with router.use(myMiddleware)
    • Built-in pretty-logging: prettyLogMiddleware()
  • Redirects Redirect not defined routes with
    router.redirect("/", "/login"),
    
0
likes
130
points
160
downloads

Publisher

verified publishernamzug.dev

Weekly Downloads

Build simple systems with Dart + HTMX

Repository (GitHub)

Topics

#server #htmx

Documentation

API reference

License

MIT (license)

Dependencies

htmleez, http_parser, mime, shelf, stack_trace

More

Packages that depend on htmdart