* feat: add middleware example demonstrating route groups Self-contained example covering v.Use(), v.Group(), nested groups, Group.Use(), and middleware chaining with role-based access control. * feat: add per-action middleware via WithMiddleware ActionOption Reuses the existing Middleware type so the same auth/logging functions work at both page and action level. Middleware runs after CSRF and rate-limit checks, with full access to session and signals. * feat: add RedirectView helper and refactor session example to use middleware RedirectView lets middleware abort and redirect in one step. The session example now uses an authRequired middleware on a route group instead of an inline check inside the view. * fix: remove dead code, fix double Load and extractParams mismatch - Remove componentRegistry (written, never read) - Remove unused signal methods: Bytes, Int64, Float - Remove unreachable nil check in registerCtx - Simplify injectRouteParams (extractParams already returns fresh map) - Fix double sync.Map.Load in injectSignals - Merge Shutdown/shutdown into single method - Inline currSessionNum - Fix extractParams: mismatched literal segment now returns nil - Minor: new(bytes.Buffer), go c.Sync(), genRandID reads 4 bytes
84 lines
1.9 KiB
Go
84 lines
1.9 KiB
Go
package via
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/ryanhamamura/via/h"
|
||
)
|
||
|
||
// Signal represents a value that is reactive in the browser. Signals
|
||
// are synct with the server right before an action triggers.
|
||
//
|
||
// Use Bind() to connect a signal to an input and Text() to display it
|
||
// reactively on an html element.
|
||
type signal struct {
|
||
id string
|
||
val any
|
||
changed bool
|
||
err error
|
||
}
|
||
|
||
// ID returns the signal ID
|
||
func (s *signal) ID() string {
|
||
return s.id
|
||
}
|
||
|
||
// Err returns a signal error or nil if it contains no error.
|
||
//
|
||
// It is useful to check for errors after updating signals with
|
||
// dinamic values.
|
||
func (s *signal) Err() error {
|
||
return s.err
|
||
}
|
||
|
||
// Bind binds this signal to an input element. When the input changes
|
||
// its value the signal updates in real-time in the browser.
|
||
//
|
||
// Example:
|
||
//
|
||
// h.Input(h.Type("number"), mysignal.Bind())
|
||
func (s *signal) Bind() h.H {
|
||
return h.Data("bind", s.id)
|
||
}
|
||
|
||
// Text binds the signal value to an html span element as text.
|
||
//
|
||
// Example:
|
||
//
|
||
// h.Div(mysignal.Text())
|
||
func (s *signal) Text() h.H {
|
||
return h.Span(h.Data("text", "$"+s.id))
|
||
}
|
||
|
||
// SetValue updates the signal’s value and marks it for synchronization with the browser.
|
||
// The change will be propagated to the browser using *Context.Sync() or *Context.SyncSignals().
|
||
func (s *signal) SetValue(v any) {
|
||
s.val = v
|
||
s.changed = true
|
||
s.err = nil
|
||
}
|
||
|
||
// String return the signal value as a string.
|
||
func (s *signal) String() string {
|
||
return fmt.Sprintf("%v", s.val)
|
||
}
|
||
|
||
// Bool tries to read the signal value as a bool.
|
||
// Returns the value or false on failure.
|
||
func (s *signal) Bool() bool {
|
||
val := strings.ToLower(s.String())
|
||
return val == "true" || val == "1" || val == "yes" || val == "on"
|
||
}
|
||
|
||
// Int tries to read the signal value as an int.
|
||
// Returns the value or 0 on failure.
|
||
func (s *signal) Int() int {
|
||
if n, err := strconv.Atoi(s.String()); err == nil {
|
||
return n
|
||
}
|
||
return 0
|
||
}
|
||
|