Cleaned up conditional rendering.

This commit is contained in:
mikestefanello 2025-03-29 09:40:04 -04:00
parent 02236266f1
commit c8db468292
5 changed files with 71 additions and 56 deletions

View file

@ -27,17 +27,13 @@ func JS(r *ui.Request) Node {
}) })
` `
var csrf Node
if len(r.CSRF) > 0 {
csrf = Script(Raw(fmt.Sprintf(htmxCSRF, r.CSRF)))
}
return Group{ return Group{
Script(Src("https://unpkg.com/htmx.org@2.0.0/dist/htmx.min.js")), Script(Src("https://unpkg.com/htmx.org@2.0.0/dist/htmx.min.js")),
Script(Src("https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"), Defer()), Script(Src("https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"), Defer()),
Script(Raw(htmxErr)), Script(Raw(htmxErr)),
csrf, Iff(len(r.CSRF) > 0, func() Node {
return Script(Raw(fmt.Sprintf(htmxCSRF, r.CSRF)))
}),
} }
} }

View file

@ -49,6 +49,10 @@ func About(ctx echo.Context) error {
Title: "Ent", Title: "Ent",
Body: "Simple, yet powerful ORM for modeling and querying data. Visit <a href=\"https://entgo.io/\">entgo.io</a> to learn more.", Body: "Simple, yet powerful ORM for modeling and querying data. Visit <a href=\"https://entgo.io/\">entgo.io</a> to learn more.",
}, },
{
Title: "Gomponents",
Body: "HTML components written in pure Go. They render to HTML 5, and make it easy for you to build reusable components. Visit <a href=\"https://gomponents.com/\">gomponents.com</a> to learn more.",
},
}, },
), ),
} }

View file

@ -15,26 +15,27 @@ func ContactUs(ctx echo.Context, form *forms.Contact) error {
r.Title = "Contact us" r.Title = "Contact us"
r.Metatags.Description = "Get in touch with us." r.Metatags.Description = "Get in touch with us."
g := make(Group, 0) g := Group{
Iff(r.Htmx.Target != "contact", func() Node {
if r.Htmx.Target != "contact" { return components.Message(
g = append(g, components.Message(
"is-link", "is-link",
"", "",
Group{ Group{
P(Text("This is an example of a form with inline, server-side validation and HTMX-powered AJAX submissions without writing a single line of JavaScript.")), P(Text("This is an example of a form with inline, server-side validation and HTMX-powered AJAX submissions without writing a single line of JavaScript.")),
P(Text("Only the form below will update async upon submission.")), P(Text("Only the form below will update async upon submission.")),
})) },
} )
}),
if form.IsDone() { Iff(form.IsDone(), func() Node {
g = append(g, components.Message( return components.Message(
"is-large is-success", "is-large is-success",
"Thank you!", "Thank you!",
Text("No email was actually sent but this entire operation was handled server-side and degrades without JavaScript enabled."), Text("No email was actually sent but this entire operation was handled server-side and degrades without JavaScript enabled."),
)) )
} else { }),
g = append(g, form.Render(r)) Iff(!form.IsDone(), func() Node {
return form.Render(r)
}),
} }
return r.Render(layouts.Primary, g) return r.Render(layouts.Primary, g)

View file

@ -18,17 +18,25 @@ func Home(ctx echo.Context, posts *models.Posts) error {
r.Metatags.Description = "This is the home page." r.Metatags.Description = "This is the home page."
r.Metatags.Keywords = []string{"Software", "Coding", "Go"} r.Metatags.Keywords = []string{"Software", "Coding", "Go"}
g := make(Group, 0) // This pages helps to illustrate the different options you can take when using HTMX to introduce interactivity
// to your web application. The following three options are available, but here, we're opting for the first one.
// 1) Highly-optimized and progressive enhancement:
// This is highly-optimized because the server is doing the least amount of work possible, only rendering
// the least amount possible based on the incoming request. It's possible that even your route handler would
// want to check the HTMX request in order to limit what it does. With HTMX, it's possible to still return a
// normal, full page, but use hx-select to pluck out only the part you want to re-render. It requires some extra
// condition checks and code but performance is improved. Progressive enhancement refers to having a fully
// functional web app, even if JS was disabled, but providing the enhancement if JS is enabled. All of these
// examples should continue to work fine without JS.
// 2) Not optimized and progressive enhancement:
// As mentioned previously, you can remove all of these conditions, re-render the entire page for every request,
// and rely on HTMX's hx-select to only replace what you want to (ie, the posts).
// 3) Optimized and partial renderings:
// You could have a separate route that is only for fetching posts while paging, and that would render only
// that partial HTML, which HTMX would then use to inject in to this page.
if r.Htmx.Target != "posts" { headerMsg := func() Node {
var hello string return Group{
if r.IsAuth {
hello = fmt.Sprintf("Hello, %s", r.AuthUser.Name)
} else {
hello = "Hello"
}
g = append(g,
Section( Section(
Class("hero is-info welcome is-small mb-5"), Class("hero is-info welcome is-small mb-5"),
Div( Div(
@ -37,7 +45,10 @@ func Home(ctx echo.Context, posts *models.Posts) error {
Class("container"), Class("container"),
H1( H1(
Class("title"), Class("title"),
Text(hello), Iff(r.IsAuth, func() Node {
return Text(fmt.Sprintf("Hello, %s", r.AuthUser.Name))
}),
If(!r.IsAuth, Text("Hello")),
), ),
H2( H2(
Class("subtitle"), Class("subtitle"),
@ -49,20 +60,24 @@ func Home(ctx echo.Context, posts *models.Posts) error {
), ),
H2(Class("title"), Text("Recent posts")), H2(Class("title"), Text("Recent posts")),
H3(Class("subtitle"), Text("Below is an example of both paging and AJAX fetching using HTMX")), H3(Class("subtitle"), Text("Below is an example of both paging and AJAX fetching using HTMX")),
) }
} }
g = append(g, posts.Render(r.Path(routenames.Home))) filesMsg := func() Node {
return Message(
if r.Htmx.Target != "posts" {
g = append(g, Message(
"is-small is-warning mt-5", "is-small is-warning mt-5",
"Serving files", "Serving files",
Group{ Group{
Text("In the example posts above, check how the file URL contains a cache-buster query parameter which changes only when the app is restarted. "), Text("In the example posts above, check how the file URL contains a cache-buster query parameter which changes only when the app is restarted. "),
Text("Static files also contain cache-control headers which are configured via middleware."), Text("Static files also contain cache-control headers which are configured via middleware."),
}, },
)) )
}
g := Group{
Iff(r.Htmx.Target != "posts", headerMsg),
posts.Render(r.Path(routenames.Home)),
Iff(r.Htmx.Target != "posts", filesMsg),
} }
return r.Render(layouts.Primary, g) return r.Render(layouts.Primary, g)

View file

@ -15,19 +15,18 @@ func AddTask(ctx echo.Context, form *forms.Task) error {
r.Title = "Create a task" r.Title = "Create a task"
r.Metatags.Description = "Test creating a task to see how it works." r.Metatags.Description = "Test creating a task to see how it works."
g := make(Group, 0) g := Group{
Iff(r.Htmx.Target != "task", func() Node {
if r.Htmx.Target != "task" { return components.Message(
g = append(g, components.Message(
"is-link", "is-link",
"", "",
Group{ Group{
P(Raw("Submitting this form will create an <i>ExampleTask</i> in the task queue. After the specified delay, the message will be logged by the queue processor.")), P(Raw("Submitting this form will create an <i>ExampleTask</i> in the task queue. After the specified delay, the message will be logged by the queue processor.")),
P(Text("See pkg/tasks and the README for more information.")), P(Text("See pkg/tasks and the README for more information.")),
})) })
}),
form.Render(r),
} }
g = append(g, form.Render(r))
return r.Render(layouts.Primary, g) return r.Render(layouts.Primary, g)
} }