-
-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a Iff
helper
#172
Add a Iff
helper
#172
Conversation
Hi @JulienTant , thank you for the PR! I've done something very similar in #99, and actually attempted to resolve this in #101 and #156 as well. I've never reached a solution I really like, essentially because I think it's confusing to have two different conditional functions from a user perspective. I really would have liked the foresight to just implement Between your solution here and the |
I think I like having two separate options because not all the If(things == nil, () => P(g.Text("No things")) Looking at the stdlib's As an alternative, we could promote a new callback type and it works natively with type NodeCallback func() g.Node
func (f NodeCallback) Render(w io.Writer) error {
return f().Render(w)
}
func TestIfWithNodeCallback(t *testing.T) {
t.Run("return the nil case without panicking", func(t *testing.T) {
var nilable *string
n := g.El("div",
g.If(nilable != nil, NodeCallback(func() g.Node {
return g.El("span", g.Text(*nilable))
})),
g.If(nilable == nil, NodeCallback(func() g.Node {
return g.El("span", g.Text("No messages."))
})),
)
assert.Equal(t, "<div><span>No messages.</span></div>", n)
})
t.Run("returns the non nil case", func(t *testing.T) {
var notNil *string = new(string)
*notNil = "You lost your hat!"
n := g.El("div",
g.If(notNil != nil, NodeCallback(func() g.Node {
return g.El("span", g.Text(*notNil))
})),
g.If(notNil == nil, NodeCallback(func() g.Node {
return g.El("span", g.Text("No messages."))
})),
)
assert.Equal(t, "<div><span>You lost your hat!</span></div>", n)
})
} |
@JulienTant I've been thinking about this. Your What if we instead adopted a pattern of a function that is immediately evaluated? Something like this: https://go.dev/play/p/mlByJAc-Upp package main
import (
"os"
g "github.com/maragudk/gomponents"
)
func main() {
_ = g.If(true, func() g.Node {
return g.El("div")
}()).Render(os.Stdout)
} I'm not sure how to make that work with the nil pointer panics without having another Otherwise, I'm leaning towards adding your |
Sidenote: I like the conciseness of |
As you mentioned, having a self-executing function here would result in the same problem when trying to deal with var n *string
_ = g.If(n != nil, func() g.Node {
return g.Text(n) // will panic
}()).Render(os.Stdout) I prefer func IfFunc(f func() bool, n g.Node) g.Node {
if (f()) {
return n
}
return nil
}) I think the big advantage of this solution is that it does not require any overhead for devs compared to the Lazy approach where you have to specify if it's an element or an attribute |
@JulienTant I think I'm convinced about going with I'll merge this and update some docs and readmes, and then release it sometime soon. |
I ran into some situation where I want to conditionally render a node if some variable is not nil and obviously I got a panic
In this situation, go will interpret the code of the second
if
regardless of the condition because the code itself is not in a condition.This PR introduces a new
Iff
helper that accepts a callback. The callback content is only interpreted when it's called, making the code safe:I'm aware of the
Lazy
effort on the side, but I guess this is no a breaking change and can still exist in addition to theLazy
effort.