Components are the building blocks of Gong applications. They encapsulate the UI and its behavior, making it easier to manage and reuse code. By using components, developers can create modular, maintainable, and scalable applications.
A Component in Gong is a Go type that implements one or more Gong-defined interfaces. Gong provides the following interfaces for customizing your components:
The View interface defines the initial templ.Component that renders when a user accesses your application. It is the only required interface for all components and is where most of your UI will be defined.
type View interface {
View() templ.Component
}
Example:
type SimpleViewComponent struct {}
templ (c SimpleViewComponent) View() {
<div>
Hello, Gong!
</div>
}
The Action interface defines how a component handles user interactions. Actions typically update server-side state and replace existing Target content. Actions can also be used for lazy-loading data.
type Action interface {
Action() templ.Component
}
Example:
type ButtonComponent struct {}
templ (c ButtonComponent) Action() {
{{
fmt.Println("Button clicked!")
}}
}
templ (c ButtonComponent) View() {
@gong.Button() {
Click Me
}
}
The Loader interface defines data loading operations. This is useful for expensive operations that fetch crucial data for your component, particularly important for nested components.
type Loader interface {
Loader(ctx context.Context) any
}
Example:
type DataLoaderComponent struct {}
func (c DataLoaderComponent) Loader(ctx context.Context) any {
return fetchNameFromDB(ctx)
}
templ (c DataLoaderComponent) View() {
<div>
{ gong.LoaderData[string](ctx) }
</div>
}
You can access loader data elsewhere in your component by calling gong.LoaderData[Type](ctx). Gong will attempt to cast the data returned by the Loader to the specified Type and will panic if the types are incompatible. Never call the Loader function directly from your component.
The Head interface allows you to define a custom <head> element that replaces Gong's default head element.
type Head interface {
Head() templ.Component
}
Example:
type CustomIndexComponent struct {}
templ (c CustomIndexComponent) View() {}
templ (c CustomIndexComponent) Head() {
<head>
<title>Custom Page</title>
</head>
}
When implementing a custom Head, ensure you include this script tag to load the HTMX library:
<script
src="https://unpkg.com/htmx.org@2.0.4"
integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+"
crossorigin="anonymous"
></script>
The Head will only be used if it is implemented by the Component in the root level Route.
Errors can be captured and handled through a centralized error handler in your Gong application.
svr := gong.NewServer(gong.ServerWithErrorHandler(func(ctx context.Context, err error) {
log.Println(err)
// Additional error handling can be implemented here
}))
Components can be nested within other components to create complex UI hierarchies. This approach promotes code reuse and maintainable architecture.
In order to properly configure a nested component, the child component must be set as a publicly accessable field within the parent component.
type ParentComponent struct {
Child gong.Component
}
func NewParentComponent(child gong.Component) gong.Component {
return gong.NewComponent(ParentComponent{
Child: child,
})
}
To render the child component, simply render it within your templ function like any other templ.Component.
templ (c ParentComponent) View() {
<div>
This is the parent!
@c.Child
</div>
}
To render a child component with data from the parent, use the WithLoaderData(any) function.
If the child component uses gong.LoaderData[Type](ctx) then the parent defined data will be used.
templ (c ParentComponent) View() {
<div>
This is the parent!
@c.Child.WithLoaderData("Hello Child")
</div>
}
To render a child component with a loader function from the parent, use the WithLoaderFunc(LoaderFunc) function.
If the child component uses gong.LoaderData[Type](ctx) then the parent defined loader will be used.
templ (c ParentComponent) View() {
<div>
This is the parent!
@c.Child.WithLoaderFunc(func(ctx context.Context) any {
return "Hello Child"
})
</div>
}