about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJulien Tant2024-06-19 01:43:52 -0700
committerGitHub2024-06-19 10:43:52 +0200
commitdafb3daa12160c85639fedd3eacf5aaf51127c53 (patch)
tree3070ec7a898edb927c77861f1747d1d06a7f9068
parenta75b25333d94bef710bbc05a95baa2e77b61bca9 (diff)
downloadgomponents-dafb3daa12160c85639fedd3eacf5aaf51127c53.tar.lz
gomponents-dafb3daa12160c85639fedd3eacf5aaf51127c53.tar.zst
gomponents-dafb3daa12160c85639fedd3eacf5aaf51127c53.zip
Add an `Iff` helper (#172)
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

```go
// package viewmodels
type SomePage struct {
	s *string
}

// package views
func SomePage (vm viewmodels.SomePage) g,Node {
	return Div(
		If(vm.s == nil, Text("s is nil"),
		If(vm.s !- nil, Text("s is " + vm.s), // this will panic when `s` is nil
	)
}
```

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:

```go
// package viewmodels
type SomePage struct {
	s *string
}

// package views
func SomePage (vm viewmodels.SomePage) g,Node {
	return Div(
		Iff(vm.s == nil, func () g.Node { return Text("s is nil") },
		Iff(vm.s !- nil, func () g.Node { return Text("s is " + vm.s) },
	)
}
```

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 the `Lazy` effort.

Co-authored-by: Markus Wüstenberg <markus@maragu.dk>
-rw-r--r--gomponents.go11
-rw-r--r--gomponents_test.go29
2 files changed, 40 insertions, 0 deletions
diff --git a/gomponents.go b/gomponents.go
index 7b73a3b..ae7cadc 100644
--- a/gomponents.go
+++ b/gomponents.go
@@ -254,9 +254,20 @@ func Group(children []Node) Node {
 
 // If condition is true, return the given Node. Otherwise, return nil.
 // This helper function is good for inlining elements conditionally.
+// If your condition and node involve a nilable variable, use iff because
+// go will evaluate the node regardless of the condition.
 func If(condition bool, n Node) Node {
 	if condition {
 		return n
 	}
 	return nil
 }
+
+// Iff execute the function f if condition is true, otherwise return nil.
+// it is the preferred way to conditionally render a node if the node involves a nilable variable.
+func Iff(condition bool, f func() Node) Node {
+	if condition {
+		return f()
+	}
+	return nil
+}
diff --git a/gomponents_test.go b/gomponents_test.go
index 32304f3..df5da4f 100644
--- a/gomponents_test.go
+++ b/gomponents_test.go
@@ -284,3 +284,32 @@ func ExampleIf() {
 	_ = e.Render(os.Stdout)
 	// Output: <div><span>You lost your hat!</span></div>
 }
+
+func TestIff(t *testing.T) {
+	t.Run("returns node if condition is true", func(t *testing.T) {
+		n := g.El("div", g.Iff(true, func() g.Node {
+			return g.El("span")
+		}))
+		assert.Equal(t, "<div><span></span></div>", n)
+	})
+
+	t.Run("returns nil if condition is false", func(t *testing.T) {
+		n := g.El("div", g.Iff(false, func() g.Node {
+			return g.El("span")
+		}))
+		assert.Equal(t, "<div></div>", n)
+	})
+}
+
+func ExampleIff() {
+	var nillableVariable *struct {
+		str string
+	}
+	e := g.El("div",
+		g.Iff(nillableVariable != nil, func() g.Node {
+			return g.Text(nillableVariable.str)
+		}),
+	)
+	_ = e.Render(os.Stdout)
+	// Output: <div></div>
+}