v0.5.7 beta
Back-end UI Framework

for feature-rich, secure, and fast web apps in Go

Tutorial

Dynamic Content

Desired catalog page structure:

  • Main: Show the list of categories
  • Category: Show a list of items in the category
    • Item Card: Pop-up

1. Create templates for all path options

Catalog Main Page

./catalog/main.templ

package catalog

import "github.com/doors-dev/doors"

templ main() {
	<h1>Catalog</h1>

	// href to  test category page
	@doors.AHref{
		Model: Path{
			IsCat: true,
			CatId: "test_cat",
		},
	}
	<a>Test Category</a>
}

./catalog/category.templ

package catalog

import "github.com/doors-dev/doors"

templ category() {
	<h1>Category</h1>
	// back to main
	@doors.AHref{
		Model: Path{
			IsMain: true,
		},
	}
	<a>Go back</a>
}

2. Enable dynamic page updates on path change

Save the path beam to the page field.

Beam represents a reactive, changing value stream. The framework provides a beam that holds the path model.

type catalogPage struct {
  // add fields
	path doors.SourceBeam[Path]
}

/* ... */

func (c *catalogPage) Render(b doors.SourceBeam[Path]) templ.Component {
	// save it
	c.path = b
	return common.Template(c)
}

Now use the path beam inside the body to enable dynamic updates explicitly.

./catalog/page.templ

templ (c *catalogPage) Body() {
	// doors.E evaluates the function and renders the return value
	@doors.E(func(ctx context.Context) templ.Component {
		// initialize dynamic container
		door := doors.Door{}
		// subscribe to path updates
		c.path.Sub(ctx, func(ctx context.Context, p Path) bool {
			// depending on the path variant marker, set the door content.
			if p.IsMain {
				door.Update(ctx, main())
			} else {
				door.Update(ctx, category())
			}
			// false means not done; keep sub active
			return false
		})
		// render dynamic container
		return &door
	})
}

Visit the catalog page and experience our first dynamic page update! Congrats!

Refactor to @doors.Sub helper

Beam and door are basic building blocks. But sometimes you want less control (and boilerplate), so let’s refactor the Body() method to use a helper component that combines beam and door.

templ (c *catalogPage) Body() {
    // subscribe helper component, updates node based on func output
	@doors.Sub(c.path, func(p Path) templ.Component {
		if p.IsMain {
			return main()
		}
		return category()
	})
}

It does precisely the same under the hood.

Since we have only one component, we can replace the templ function with a standard one that follows the interface.

func (c *catalogPage) Body() templ.Component {
	return doors.Sub(c.path, func(p Path) templ.Component {
		if p.IsMain {
			return main()
		}
		return category()
	})
}

Next: Derive Beam