DEV Community

Fernando Correa de Oliveira
Fernando Correa de Oliveira

Posted on • Edited on

Cromponent new features

Cromponent πŸŽ‰ now lets your components bind cookies, query-string params, headers and HTTP-auth credentials directly in the method signature πŸͺβ“πŸ“‘πŸ” and push live HTML over WebSockets with two tiny hooks πŸ”ŒπŸ›°οΈ.
Paired with HTMX on the client, that means real-time Raku apps with zero JavaScript πŸš€. Below you’ll find the new API plus a complete β€œlive poll” example. Dive in! πŸŠβ€β™‚οΈ

βΈ»

  1. Why Cromponent? πŸ€”βœ¨

Cromponent glues together Cro’s reactive HTTP / WebSocket server and Cro Templates to give Raku developers a component abstraction Γ  la modern SPA frameworks – but rendered on the server and streamed as HTML fragments.
The latest release focuses on state & interaction: declarative request-context binding and built-in WebSocket wiring. ❀️‍πŸ”₯

βΈ»

  1. Prerequisites at a Glance πŸ“šπŸ”Ž
Layer Role 1-Liner
HTMX ⚑ Enriches plain HTML with AJAX, SSE & WebSockets via attributes. hx-put, hx-target, hx-ext="ws", …
Cro 🧩 Reactive HTTP/WebSocket server & router for Raku. route { … }
Cro Templates πŸ–‹οΈ HTML-centric templating DSL compiled on first use. template 'view.crotmp', %data
Red ORM πŸ—„οΈ DB-agnostic ORM with migrations & relationships. model Foo { has Int $.id is serial … }
Dynamic vars 🌐 $*foo passes per-request context through deep calls. $*user in our example
  1. New API: Context Traits πŸͺβ“πŸ“‘πŸ”

Any method tagged is accessible becomes an endpoint and auto-binds request data:

method profile(
    Str :$uuid   is cookie,
    Str :$q      is query,
    Str :$accept is header,
    Str :$creds  is auth
) is accessible { ... }
Enter fullscreen mode Exit fullscreen mode
  • Zero plumbing πŸ› οΈ
  • Type-safe input πŸ›‘οΈ
  • Self-documenting signatures πŸ“œ

Route pattern:

/ComponentName/<ids…>/profile
Enter fullscreen mode Exit fullscreen mode
  1. New API: Automatic WebSockets πŸ”ŒπŸ›°οΈ
    1. Channel key – method IDS { $!id } groups browsers in the same room.
    2. Re-render hook – method REDRAW(:$*user is cookie) { $.Str } returns fresh HTML when you redraw $component.
    3. <|WebSocket> cromponent adds hx-ext="ws" ws-connect="/ws" and the htmx-ws extension swaps fragments for you.

No manual frames, pings or reconnect loops – Cro & HTMX handle all that jazz. 🎷

βΈ»

  1. Building a Live Poll πŸ—³οΈβš‘

5.1. Router & DB Setup πŸ›£οΈ

my $routes = route {
    red-defaults "SQLite";
    PollItem.^add-cromponent-routes;   # auto REST πŸ†“
    WebSocket.^add-cromponent-routes;  # auto WS  πŸ†“

    get -> 'polls', Str :$*user is cookie = UUID.new.Str {
        response.set-cookie: :name<user>, :value($*user);
        template 'polls.crotmp', { :$*user, :@polls = Poll.^all }
    }
}
Cro::HTTP::Server.new(:host<0.0.0.0>, :port(2000), :$routes).start;
Enter fullscreen mode Exit fullscreen mode

First visit ➑️ generates UUID ➑️ saves in πŸͺ ➑️ available as $*user.

5.2. Poll Component πŸ“Š

model Poll does Cromponent {
    has UInt $.id    is serial;
    has Str  $.descr is column;
    has      @.items is relationship(*.poll-id, :model<PollItem>);
    has      @.votes is relationship(*.poll-id, :model<PollVote>);

    method LOAD($id)       { Poll.^load: $id }  # rebuild
    method IDS             { $!id }             # WS room
    method REDRAW(:$*user) { $.Str }            # push update

    method did-user-vote($who = $*user) {
        ?@.votes.first: *.user eq $who
    }

    method RENDER { … }   # the template
}
Enter fullscreen mode Exit fullscreen mode

5.3. PollItem Component βœ…

method vote(Str :$*user is cookie)
      is accessible { :http-method<PUT>, :returns-cromponent } {
    red-do :transaction, {
        $!votes++;
        self.^save;
        $!poll.votes.create: :$*user;
        redraw $!poll;      # broadcast 🌍
        $!poll
    }
}
Enter fullscreen mode Exit fullscreen mode

HTMX button:

<button
  hx-put="/poll-item/42/vote"
  hx-target="closest .poll"
  hx-swap="outerHTML">πŸ—³οΈ Vote</button>
Enter fullscreen mode Exit fullscreen mode

5.4. WebSocket-Enabled Page πŸ–₯️

<|Boilerplate(:title('Polls πŸ—³οΈ'), :htmx, :style-sheets('/css'))>
  <|WebSocket>
    <h6>Logged in as <.user> πŸ™‹β€β™‚οΈ</h6>
    <@.polls><&HTML($_)></@>
    <a href="/polls">All polls πŸ“‹</a>
  </|>
</|>
Enter fullscreen mode Exit fullscreen mode
  1. Generated Endpoints Recap πŸ›€οΈ
Method Path Source
GET /polls manual route ❌
GET /polls/<id> manual route ❌
PUT /poll-item/<id>/vote PollItem.vote βœ…
GET /cromponent-ws WebSocket βœ…

One call to .^add-cromponent-routes ➑️ endpoints galore! 🎊

βΈ»

  1. Key Take-aways πŸ’‘
  2. Context traits (is cookie πŸͺ, is query ❓, is header πŸ“‘, is auth πŸ”) shred boilerplate.
  3. IDS + REDRAW inject WebSocket magic with two methods. ✨
  4. Everything lives in one file: model + template + behaviour – still plain Raku, still testable. πŸ§ͺ

Result: back-end-driven UX with instant updates and zero JavaScript. πŸš€

βΈ»

  1. Further Reading πŸ“š
  2. 🌟 Cromponent β†’ https://github.com/FCO/Cromponent
  3. πŸ“œ Cro docs β†’ https://cro.services
  4. πŸ—„οΈ Red ORM β†’ https://github.com/FCO/Red
  5. ⚑ HTMX β†’ https://htmx.org
  6. πŸ”Œ htmx-ws extension β†’ https://htmx.org/extensions/ws/
  7. 🧠 Dynamic vars in Raku β†’ https://docs.raku.org/language/variables#Dynamic_variables

Happy hacking – and may your components stay Crom-pact! πŸ¦€πŸŽˆ

Top comments (0)