robertwayne / axum-htmx

A set of htmx extractors, responders, and request guards for axum.
Apache License 2.0
185 stars 9 forks source link

HxBoostedBy macro to avoid redundant if-else for layout #12

Open qrdwtf opened 7 months ago

qrdwtf commented 7 months ago

Hi!

I'd like to propose a macros feature in addition to HxBoosted extractor.

Currently with axum-htmx we use HxBoosted like this:

async fn get_index(HxBoosted(boosted): HxBoosted) -> impl IntoResponse {
    if boosted {
        // Send a template extending from _partial.html
    } else {
        // Send a template extending from _base.html
    }
}

I propose to add a macro, then it will be:

#[hx_boosted_by(with_layout)]
async fn get_index() -> Html<String> {
    // some impl here

    // <-- page html -->
}

fn with_layout(Html(html): Html<String>) -> Html<String> {
    // Wrap page html into layout if called
}

What this macro does is transforming that function to:

async fn get_index(HxBoosted(boosted): HxBoosted) -> Html<String> {
    // some impl here

    if boosted {
        // <-- page html -->
    } else {
        // <-- with_layout(page html) -->
    }
}

I don't have much experience with Rust but managed to write something close to that macro in this axum-htmx-derive repo. Didn't publish yet, don't think it requires separate package - but needs to be included in axum-htmx (with feature flag).

Currently it has 2 macros: hx_boosted_by and hx_boosted_by_async - if you have any idea on how it can be merged I'm here :)

Let me know what you think.

robertwayne commented 7 months ago

I'm okay with this. I think reducing the boilerplate is worth the additional complexity and I'm willing to maintain it long-term. It fits within the spirit of the library.

Are you interested in starting a draft PR for this here? I imagine that most of your downstream work should be generally transferrable, though I haven't had a chance to look through your repo yet.

qrdwtf commented 7 months ago

That's great! Sure, I'll make a PR.

tibbe commented 4 days ago

Having used htmx + askama for a little while now the duplication and awkwardness of partial vs full pages is one of, if not the biggest pain point. Here's what it looks like for me:

Constraints:

This ends in sadness. My workflow today looks as follows:

  1. The above ifs in every router plus 2 askama Template structs.
  2. Two .html template files to go with the above structs e.g.

index.html:

{% extends "../layouts/base.html" %}

{% block content %}
{% include "_index.html" %}
{% endblock %}

_index.html:

<h1>Register</h1>
<form hx-post="/register" hx-target="#container" hx-swap="innerHTML" hx-push-url="true">
  <fieldset>
    <div class="form-group">
      <label for="email">Email</label>
      <input class="form-control" name="email" />
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input class="form-control" name="password" type="password" />
    </div>
    <input class="btn btn-primary" type="submit" />
  </fieldset>
</form>

These two are needed as the {% extends "../layouts/base.html" %} cannot be made conditional.

This is even before I have tackled including the user in the base template, which will make things even worse because the user only really needs to be loaded in the non-htmx case (as it doesn't change in the htmx case, typically). Also, every route would have to care about the user even though that kind of concern ideally should be dealt with "globally" by some middleware.

Any ideas?