Guard
type
Guard is a stubbed route guard for non-WASM builds.
Type Definition:
func(map[string]string) bool
Route
struct
Route describes a route mapping in stub builds.
Fields:
- Path (string)
- Component (func() core.Component)
- Guards ([]Guard)
- Children ([]Route)
RegisteredRoute
struct
RegisteredRoute describes a registered route in stub builds.
Fields:
- Template (string)
- Path (string)
- Params ([]string)
- Children ([]RegisteredRoute)
Reset
function
Reset is a no-op in stub builds.
Show/Hide Function
Body
{}
RegisterRoute
function
RegisterRoute is a no-op in stub builds.
Parameters:
References:
Show/Hide Function
Body
{}
Navigate
function
Navigate is a no-op in stub builds.
Parameters:
Show/Hide Function
Body
{}
RegisteredRoutes
function
RegisteredRoutes returns nil in stub builds.
Returns:
Show/Hide Function
Body
{ return nil }
ExposeNavigate
function
ExposeNavigate is a no-op in stub builds.
Show/Hide Function
Body
{}
InitRouter
function
InitRouter is a no-op in stub builds.
Show/Hide Function
Body
{}
trailingComponent
struct
Methods:
Render
Returns:
Show/Hide
Method Body
{ return "" }
Mount
Show/Hide
Method Body
{}
Unmount
Show/Hide
Method Body
{}
OnMount
Show/Hide
Method Body
{}
OnUnmount
Show/Hide
Method Body
{}
GetName
Returns:
Show/Hide
Method Body
{ return "trailing" }
GetID
Returns:
Show/Hide
Method Body
{ return "" }
SetSlots
Parameters:
Show/Hide
Method Body
{}
TestNavigateTrailingSlash
function
Parameters:
Show/Hide Function
Body
{
Reset()
RegisterRoute(Route{Path: "/trail", Component: func() core.Component { return &trailingComponent{} }})
Navigate("/trail/")
if _, ok := currentComponent.(*trailingComponent); !ok {
t.Fatalf("expected trailingComponent with trailing slash, got %T", currentComponent)
}
}
TestNavigateTrailingSlashNotFound
function
Parameters:
Show/Hide Function
Body
{
Reset()
RegisterRoute(Route{Path: "/trail", Component: func() core.Component { return &trailingComponent{} }})
called := false
NotFoundCallback = func(p string) { called = true }
Navigate("/trail/extra")
if !called {
t.Fatalf("expected NotFoundCallback for extra path, got none")
}
NotFoundCallback = nil
}
Guard
type
Guard is a function that determines whether navigation to a route is
permitted based on the provided parameters.
Type Definition:
func(map[string]string) bool
Route
struct
Route describes a routing rule that maps a path to a component and optional
guards or child routes.
Fields:
- Path (string)
- Component (func() core.Component)
- Guards ([]Guard)
- Children ([]Route)
route
struct
Fields:
- pattern (string)
- regex (*regexp.Regexp)
- paramNames ([]string)
- component (core.Component)
- loader (func() core.Component)
- children ([]route)
- guards ([]Guard)
RegisteredRoute
struct
RegisteredRoute describes a registered route in a navigable tree form.
Fields:
- Template (string) - json:"template"
- Path (string) - json:"path"
- Params ([]string) - json:"params"
- Children ([]RegisteredRoute) - json:"children"
Reset
function
Reset clears the router's registered routes and current component.
It is primarily intended for use in tests to ensure a clean state.
Show/Hide Function
Body
{
routes = nil
currentComponent = nil
}
RegisterRoute
function
RegisterRoute adds a new Route to the router's configuration.
Parameters:
References:
Show/Hide Function
Body
{
routes = append(routes, buildRoute(r))
}
buildRoute
function
Parameters:
Returns:
References:
-
Route
(v1/router)
-
route
(v1/router)
Show/Hide Function
Body
{
segments := strings.Split(strings.Trim(r.Path, "/"), "/")
regexParts := make([]string, len(segments))
paramNames := []string{}
for i, segment := range segments {
if strings.HasPrefix(segment, ":") {
name := strings.TrimPrefix(segment, ":")
paramNames = append(paramNames, name)
regexParts[i] = "([^/]+)"
} else {
regexParts[i] = regexp.QuoteMeta(segment)
}
}
pathRegex := strings.Join(regexParts, "/")
suffix := "/?$"
if len(r.Children) > 0 {
suffix = "(?:/|$)"
}
if pathRegex == "" {
suffix = "$"
}
pattern := "^/" + pathRegex + suffix
rt := route{
pattern: r.Path,
regex: regexp.MustCompile(pattern),
paramNames: paramNames,
loader: r.Component,
guards: r.Guards,
}
for _, child := range r.Children {
rt.children = append(rt.children, buildRoute(child))
}
return rt
}
RegisteredRoutes
function
RegisteredRoutes returns the registered routes including nested children and
resolved full paths. The data can be used for tooling and diagnostics.
Returns:
Show/Hide Function
Body
{
out := make([]RegisteredRoute, 0, len(routes))
for i := range routes {
out = append(out, snapshotRoute(&routes[i], ""))
}
return out
}
snapshotRoute
function
Parameters:
Returns:
References:
Show/Hide Function
Body
{
params := make([]string, len(r.paramNames))
copy(params, r.paramNames)
full := resolveRoutePath(parent, r.pattern)
children := make([]RegisteredRoute, len(r.children))
for i := range r.children {
children[i] = snapshotRoute(&r.children[i], full)
}
return RegisteredRoute{
Template: r.pattern,
Path: full,
Params: params,
Children: children,
}
}
resolveRoutePath
function
Parameters:
- parent string
- path string
Returns:
Show/Hide Function
Body
{
if path == "" {
if parent == "" {
return "/"
}
return parent
}
if strings.HasPrefix(path, "/") {
return path
}
trimmed := strings.TrimPrefix(path, "/")
if parent == "" || parent == "/" {
return "/" + trimmed
}
if strings.HasSuffix(parent, "/") {
return parent + trimmed
}
return parent + "/" + trimmed
}
routeParamReceiver
interface
Methods:
SetRouteParams
Parameters:
matchRoute
function
Parameters:
- routes []route
- path string
Returns:
- *route
- []Guard
- map[string]string
Show/Hide Function
Body
{
for i := range routes {
r := &routes[i]
if matches := r.regex.FindStringSubmatch(path); matches != nil {
params := map[string]string{}
for i, name := range r.paramNames {
if i+1 < len(matches) {
params[name] = matches[i+1]
}
}
if child, guards, childParams := matchRoute(r.children, path); child != nil {
for k, v := range params {
childParams[k] = v
}
return child, append(r.guards, guards...), childParams
}
return r, r.guards, params
}
}
return nil, nil, nil
}
Navigate
function
Navigate renders the component associated with the specified path if all
route guards allow it. The provided path may include a query string which
will be parsed and passed to the component via SetRouteParams.
Parameters:
Show/Hide Function
Body
{
path := fullPath
query := ""
if idx := strings.Index(fullPath, "?"); idx != -1 {
path = fullPath[:idx]
query = fullPath[idx+1:]
}
r, guards, params := matchRoute(routes, path)
if r == nil {
if NotFoundCallback != nil {
NotFoundCallback(fullPath)
} else if NotFoundComponent != nil {
if currentComponent != nil {
core.Log().Debug("Unmounting current component: %s", currentComponent.GetName())
core.TriggerUnmount(currentComponent)
currentComponent.Unmount()
}
c := NotFoundComponent()
currentComponent = c
dom.UpdateDOM(c.GetID(), c.Render())
c.Mount()
core.TriggerMount(c)
core.TriggerRouter(fullPath)
}
return
}
for _, g := range guards {
if !g(params) {
if currentComponent == nil && path != "/" {
Navigate("/")
}
return
}
}
if r.loader != nil {
r.component = r.loader()
}
if params == nil {
params = map[string]string{}
}
if query != "" {
if values, err := url.ParseQuery(query); err == nil {
for k, v := range values {
if len(v) > 0 {
params[k] = v[0]
}
}
}
}
if receiver, ok := r.component.(routeParamReceiver); ok {
receiver.SetRouteParams(params)
}
if currentComponent != nil {
core.Log().Debug("Unmounting current component: %s", currentComponent.GetName())
core.TriggerUnmount(currentComponent)
currentComponent.Unmount()
}
currentComponent = r.component
dom.UpdateDOM(r.component.GetID(), r.component.Render())
r.component.Mount()
core.TriggerMount(r.component)
core.TriggerRouter(fullPath)
js.History().Call("pushState", nil, "", fullPath)
}
CanNavigate
function
CanNavigate reports whether the specified path matches a registered route.
Parameters:
Returns:
Show/Hide Function
Body
{
path := fullPath
if idx := strings.Index(fullPath, "?"); idx != -1 {
path = fullPath[:idx]
}
r, _, _ := matchRoute(routes, path)
return r != nil
}
ExposeNavigate
function
ExposeNavigate makes the Navigate function accessible from JavaScript and
automatically routes internal anchor clicks.
Show/Hide Function
Body
{
exposeNavigateOnce.Do(func() {
js.ExposeFunc("goNavigate", func(this js.Value, args []js.Value) any {
path := args[0].String()
Navigate(path)
return nil
})
events.On("click", js.Document(), func(evt js.Value) {
link := evt.Get("target").Call("closest", "a[href]")
if !link.Truthy() {
return
}
if t := link.Get("target").String(); t != "" && t != "_self" {
return
}
if link.Get("origin").String() != js.Location().Get("origin").String() {
return
}
path := link.Get("pathname").String() + link.Get("search").String()
if CanNavigate(path) {
evt.Call("preventDefault")
Navigate(path)
}
})
})
}
InitRouter
function
InitRouter initializes the router and begins listening for navigation
events.
Show/Hide Function
Body
{
ch := events.Listen("popstate", js.Window())
go func() {
for range ch {
path := js.Location().Get("pathname").String() + js.Location().Get("search").String()
Navigate(path)
}
}()
currentPath := js.Location().Get("pathname").String() + js.Location().Get("search").String()
Navigate(currentPath)
}
testComponent
struct
Methods:
Render
Returns:
Show/Hide
Method Body
{ return "" }
Mount
Show/Hide
Method Body
{}
Unmount
Show/Hide
Method Body
{}
OnMount
Show/Hide
Method Body
{}
OnUnmount
Show/Hide
Method Body
{}
GetName
Returns:
Show/Hide
Method Body
{ return "test" }
GetID
Returns:
Show/Hide
Method Body
{ return "" }
SetSlots
Parameters:
Show/Hide
Method Body
{}
Render
Returns:
Show/Hide
Method Body
{ return "" }
Mount
Show/Hide
Method Body
{}
Unmount
Show/Hide
Method Body
{}
OnMount
Show/Hide
Method Body
{}
OnUnmount
Show/Hide
Method Body
{}
GetName
Returns:
Show/Hide
Method Body
{ return "test" }
GetID
Returns:
Show/Hide
Method Body
{ return "" }
SetSlots
Parameters:
Show/Hide
Method Body
{}
SetRouteParams
Parameters:
Show/Hide
Method Body
{ c.params = p }
TestCanNavigate
function
Parameters:
Show/Hide Function
Body
{
Reset()
RegisterRoute(Route{Path: "/can", Component: func() core.Component { return &testComponent{} }})
if !CanNavigate("/can") {
t.Fatalf("expected true for registered route")
}
if !CanNavigate("/can?foo=bar") {
t.Fatalf("expected true for registered route with query")
}
if CanNavigate("/missing") {
t.Fatalf("expected false for unregistered route")
}
}
testComponent
struct
testComponent implements core.Component and routeParamReceiver for testing.
Fields:
- params (map[string]string)
Methods:
Render
Returns:
Show/Hide
Method Body
{ return "" }
Mount
Show/Hide
Method Body
{}
Unmount
Show/Hide
Method Body
{}
OnMount
Show/Hide
Method Body
{}
OnUnmount
Show/Hide
Method Body
{}
GetName
Returns:
Show/Hide
Method Body
{ return "test" }
GetID
Returns:
Show/Hide
Method Body
{ return "" }
SetSlots
Parameters:
Show/Hide
Method Body
{}
Render
Returns:
Show/Hide
Method Body
{ return "" }
Mount
Show/Hide
Method Body
{}
Unmount
Show/Hide
Method Body
{}
OnMount
Show/Hide
Method Body
{}
OnUnmount
Show/Hide
Method Body
{}
GetName
Returns:
Show/Hide
Method Body
{ return "test" }
GetID
Returns:
Show/Hide
Method Body
{ return "" }
SetSlots
Parameters:
Show/Hide
Method Body
{}
SetRouteParams
Parameters:
Show/Hide
Method Body
{ c.params = p }
TestNavigateQueryParams
function
Parameters:
Show/Hide Function
Body
{
Reset()
RegisterRoute(Route{Path: "/query", Component: func() core.Component { return &testComponent{} }})
Navigate("/query?key=value")
tc, ok := currentComponent.(*testComponent)
if !ok {
t.Fatalf("expected testComponent, got %T", currentComponent)
}
if tc.params["key"] != "value" {
t.Fatalf("expected query param 'key=value', got %v", tc.params)
}
}
TestNavigateNotFound
function
Parameters:
Show/Hide Function
Body
{
Reset()
called := false
NotFoundCallback = func(p string) { called = true }
Navigate("/missing")
if !called {
t.Fatalf("expected NotFoundCallback to be called")
}
NotFoundCallback = nil
}
routeComponent
struct
Methods:
Render
Returns:
Show/Hide
Method Body
{ return "" }
Mount
Show/Hide
Method Body
{}
Unmount
Show/Hide
Method Body
{}
OnMount
Show/Hide
Method Body
{}
OnUnmount
Show/Hide
Method Body
{}
GetName
Returns:
Show/Hide
Method Body
{ return "route" }
GetID
Returns:
Show/Hide
Method Body
{ return "route" }
SetSlots
Parameters:
Show/Hide
Method Body
{}
TestRegisteredRoutes
function
Parameters:
Show/Hide Function
Body
{
Reset()
RegisterRoute(Route{Path: "/static", Component: func() core.Component { return routeComponent{} }})
RegisterRoute(Route{
Path: "/users",
Component: func() core.Component { return routeComponent{} },
Children: []Route{
{
Path: ":id",
Component: func() core.Component { return routeComponent{} },
},
{
Path: ":id/profile",
Component: func() core.Component { return routeComponent{} },
},
},
})
defs := RegisteredRoutes()
if len(defs) != 2 {
t.Fatalf("expected 2 top level routes, got %d", len(defs))
}
if defs[0].Path != "/static" || len(defs[0].Params) != 0 {
t.Fatalf("unexpected static route: %+v", defs[0])
}
users := defs[1]
if users.Path != "/users" {
t.Fatalf("expected /users path, got %s", users.Path)
}
if len(users.Children) != 2 {
t.Fatalf("expected two children, got %d", len(users.Children))
}
child := users.Children[0]
if child.Path != "/users/:id" {
t.Fatalf("expected /users/:id full path, got %s", child.Path)
}
if len(child.Params) != 1 || child.Params[0] != "id" {
t.Fatalf("expected id param, got %+v", child.Params)
}
profile := users.Children[1]
if profile.Path != "/users/:id/profile" {
t.Fatalf("expected /users/:id/profile path, got %s", profile.Path)
}
}
reloadComponent
struct
Methods:
Render
Returns:
Show/Hide
Method Body
{ return "" }
Mount
Show/Hide
Method Body
{}
Unmount
Show/Hide
Method Body
{}
OnMount
Show/Hide
Method Body
{}
OnUnmount
Show/Hide
Method Body
{}
GetName
Returns:
Show/Hide
Method Body
{ return "reload" }
GetID
Returns:
Show/Hide
Method Body
{ return "" }
SetSlots
Parameters:
Show/Hide
Method Body
{}
TestNavigateReloadsRouteEachTime
function
Parameters:
Show/Hide Function
Body
{
Reset()
count := 0
RegisterRoute(Route{Path: "/reload", Component: func() core.Component {
count++
return &reloadComponent{}
}})
Navigate("/reload")
Navigate("/reload")
if count != 2 {
t.Fatalf("expected loader called twice, got %d", count)
}
}
testing import
Import example:
import "testing"
net/url import
Import example:
import "net/url"
regexp import
Import example:
import "regexp"
strings import
Import example:
import "strings"
sync import
Import example:
import "sync"
testing import
Import example:
import "testing"
testing import
Import example:
import "testing"
testing import
Import example:
import "testing"
testing import
Import example:
import "testing"