Active Link
Currently there is no active link highlighting. There are two ways to add it:
- Check the current state during render and attach styles manually, then re-render links on each path change.
- Use the
doors.ActiveAPI and let the frontend handle it automatically.
doors.Activeis part of Navigation and reuses the same indicator rules described in Indication.
Active
Add doors.Active to the navigation links:
dashboard.gox
elem (d dashboard) unitNav(days int) {
<ul>
~(for _, units := range []driver.Units{driver.Metric, driver.Imperial} {
<li>
<a
class="secondary"
(doors.ALink{
// Active link setup.
Active: doors.Active{
// Indicate activity with an attribute.
Indicator: doors.IndicateAttr("aria-current", "true"),
// Additional parameters for changing matching logic:
// PathMatcher: ...,
// QueryMatcher: ...,
},
Model: Path{
Route: Dashboard,
CityID: d.cityID,
Days: daysQuery(days),
Units: unitsQuery(units),
},
})>
~(units.String())
</a>
</li>
})
</ul>
}
Let's refactor the link into a separate component, so we can reuse the same setup in both navs:
type link struct {
city int
days int
units driver.Units
text any
}
elem (l link) Main() {
<a
class="secondary"
(doors.ALink{
Model: Path{
Route: Dashboard,
CityID: l.city,
Days: daysQuery(l.days),
Units: unitsQuery(l.units),
},
})>
~(l.text)
</a>
}
Render it in the navs:
elem (d dashboard) daysNav(units driver.Units) {
<ul>
~(for i := range 7 {
~{
days := i + 1
}
<li>
~navLink{
city: d.cityID,
days: days,
units: units,
text: <>
~(days)
~(if days == 1 {
day
} else {
days
})
</>,
}
</li>
})
</ul>
}
elem (d dashboard) unitNav(days int) {
<ul>
~(for _, units := range []driver.Units{driver.Metric, driver.Imperial} {
<li>
~navLink{
city: d.cityID,
days: days,
units: units,
text: units,
}
</li>
})
</ul>
}
The active link API reuses the indication API and lets you configure rules for path and query matching. See the Navigation documentation for more details.
After it is applied to both navigation groups:

Next: Charts
Code
dashboard.gox
package main
import (
"github.com/doors-dev/doors"
"github.com/doors-dev/gox"
"github.com/doors-dev/tutorial/driver"
)
func WeatherDashboard(path doors.Source[Path]) gox.Comp {
city := doors.DeriveBeam(path, func(p Path) int {
return p.CityID
})
days := doors.DeriveBeam(path, func(p Path) int {
return p.days()
})
units := doors.DeriveBeam(path, func(p Path) driver.Units {
return p.units()
})
return city.Bind(elem(cityID int) {
~dashboard{
cityID: cityID,
days: days,
units: units,
}
})
}
type dashboard struct {
cityID int
days doors.Beam[int]
units doors.Beam[driver.Units]
}
elem (d dashboard) Main() {
~{
city, _ := driver.Locations.CitiesGet(d.cityID)
}
~(if !city.IsValid() {
~(doors.Status(404))
<title>Not Found</title>
<h1>City Not Found</h1>
} else {
<title>~(city.Name) Weather</title>
<h1>Weather in ~(city.Name, ", ", city.Country.Name)</h1>
})
~(d.change())
~(d.menu())
}
elem (d dashboard) change() {
~>(new(doors.Door)) <>
~{
days, _ := d.days.Effect(ctx)
units, ok := d.units.Effect(ctx)
}
~(if ok {
<a
class="secondary"
(doors.ALink{
Model: Path{
Route: Selector,
Days: daysQuery(days),
Units: unitsQuery(units),
},
})
role="button">
Change
</a>
})
</>
}
elem (d dashboard) menu() {
<nav>
~(d.units.Bind(d.daysNav))
~(d.days.Bind(d.unitNav))
</nav>
}
elem (d dashboard) daysNav(units driver.Units) {
<ul>
~(for i := range 7 {
~{
days := i + 1
}
<li>
~navLink{
city: d.cityID,
days: days,
units: units,
text: <>
~(days)
~(if days == 1 {
day
} else {
days
})
</>,
}
</li>
})
</ul>
}
elem (d dashboard) unitNav(days int) {
<ul>
~(for _, units := range []driver.Units{driver.Metric, driver.Imperial} {
<li>
~navLink{
city: d.cityID,
days: days,
units: units,
text: units,
}
</li>
})
</ul>
}
type navLink struct {
city int
days int
units driver.Units
text any
}
elem (l navLink) Main() {
<a
class="secondary"
(doors.ALink{
Model: Path{
Route: Dashboard,
CityID: l.city,
Days: daysQuery(l.days),
Units: unitsQuery(l.units),
},
})>
~(l.text)
</a>
}