ALink
doors.ALink is a real anchor with Doors behavior on top.
<>
~>doors.ALink{
Model: Path{
Section: SectionHome,
},
} <a>Home</a>
</>
Model is required. It accepts:
- a path-model struct
- a
doors.Locationvalue - a custom type implementing
doors.LocationEncoder
The same three forms work in ActionLocationAssign, ActionLocationReplace, and doors.NewLocation.
LocationEncoder
doors.LocationEncoder is a one-method interface for custom navigation models:
type LocationEncoder interface {
Encode() (doors.Location, error)
}
Implement it on a domain type when you have one-off URL shapes that don't justify a full path-model struct, or when an existing type already knows its own URL form. It pairs naturally with RouteDerive on Location for the matching side — together they round-trip a custom value through the URL.
type CustomRoute struct {
ID string
Tab string
}
func (r CustomRoute) Encode() (doors.Location, error) {
q := url.Values{}
if r.Tab != "" {
q.Set("tab", r.Tab)
}
return doors.Location{
Segments: []string{"custom", r.ID},
Query: q,
}, nil
}
CustomRoute{ID: "a", Tab: "info"} now works wherever a model is accepted.
Link Behavior
ALink always navigates dynamically. A normal click is intercepted, a hook updates the current instance's location source, and the page re-routes in place. Route(...) matches the new Location — the active route swaps if a different route now matches; otherwise the existing fragment stays and reacts to the new value via its Source or Beam.
The element is also a real anchor — href is set from the encoded model. That's there for browser features (middle-click, Cmd-click, "open in new tab", "copy link"), not as a fallback for clicks. Each of those opens the URL in a fresh browser context, handled like any other initial request.
On a normal dynamic click, the client updates the browser URL to the link href before the hook request runs. If that request then fails (for example, the instance has expired), OnError runs. The default is ActionLocationReload{}, so the browser loads the URL now in the address bar, which is the link target.
Fragment
Use Fragment to append #... to the generated URL:
<>
~>doors.ALink{
Model: Path{Section: SectionDocs},
Fragment: "api",
} <a>API</a>
</>
Active
Use Active when a link should reflect the current location.
<>
~>doors.ALink{
Model: Path{
Section: SectionDashboard,
ID: f.id,
},
Active: doors.Active{
Indicator: doors.IndicateAttr("aria-current", "page"),
},
} <a>Current page</a>
</>
When the current location matches, Doors applies the indicator to the link element.
If Active.Indicator is empty, there is no active-link behavior.
Path
Active.PathMatcher controls how the path is compared.
doors.PathMatcherFull()— full path (default)doors.PathMatcherStarts()— current path starts with the link pathdoors.PathMatcherSegments(i...)— only listed segment indexes (zero-based)
Query
Active.QueryMatcher controls query-string matching. The matchers are applied in order, then Doors compares any remaining query parameters.
doors.QueryMatcherIgnoreSome(params...)— drop the listed keys from comparisondoors.QueryMatcherIgnoreAll()— drop all remaining keysdoors.QueryMatcherSome(params...)— compare only the listed keys at this stepdoors.QueryMatcherIfPresent(params...)— compare the listed keys only when present
Chain matchers with .And(...) when several steps are needed:
Active: doors.Active{
QueryMatcher: doors.QueryMatcherSome("mode").And(doors.QueryMatcherIgnoreAll()),
Indicator: doors.IndicateClass("active"),
}
This example compares only mode and ignores every other query parameter.
Fragment Match
Active.FragmentMatch includes #... in matching. Off by default.
Hook Fields
ALink is a dynamic hook, so it supports the same request-lifecycle fields as event attrs:
Scope— request scheduling and de-duplication. See Scopes.Indicator— UI feedback while the request is in flight. See Indication.Before— actions to run before the request.After— actions to run after a successful request.OnError— actions to run on failure. Defaults toActionLocationReload{}if nil.
For action types, see Actions.
Programmatic
For navigation that doesn't fit an anchor (button, wizard step, form flow), update that Source directly.
The source you get from doors.RouteModel(...) is a typed view of the current URL. Updating it encodes the new path model back into the URL, like an ALink click.
For raw locations, update doors.Router(ctx) directly.
Update
When you already know the full target model:
a.path.Update(ctx, Path{
Section: SectionDashboard,
ID: cityID,
})
Mutate
When you want to preserve part of the current path and change the rest:
type App struct {
path doors.Source[Path]
}
elem (a *App) goToCity(cityID int) {
<button
(doors.AClick{
On: func(ctx context.Context, r doors.RequestEvent[doors.PointerEvent]) bool {
a.path.Mutate(ctx, func(p Path) Path {
p.Section = SectionDashboard
p.ID = cityID
return p
})
return false
},
})>
Open city
</button>
}