type
LiveValidator
Check an unsaved field value on the server and return feedback before the author saves.
Example
package content
import (
"github.com/riducms/ridu/field"
"github.com/riducms/ridu/operation"
)
var Pricing = field.Fields{
field.Number("price"),
field.Number("salePrice").LiveValidate(checkSalePrice),
}
func checkSalePrice(
ctx operation.LiveValidationContext,
value operation.Value[float64],
) ([]operation.Issue, error) {
salePrice, hasSalePrice := value.Get()
price, hasPrice := ctx.Siblings.Get("price").NumberValue()
if !hasSalePrice || !hasPrice {
// Wait until both prices are available before comparing them.
return nil, nil
}
if salePrice < price {
return nil, nil
}
return []operation.Issue{{
Code: "sale_price",
Message: "Sale price must be lower than the regular price.",
}}, nil
}Arguments
| Argument | Type | Description |
|---|---|---|
ctx | operation.LiveValidationContext | The request, user, exact locale, readable form values, saved values, and a read-only document reader. |
value | operation.Value[T] | The current typed value after retaining omitted update fields. Get returns the value and whether it is present. Missing and null values are empty; use ctx.Input.Lookup to distinguish omitted input from an explicit null. |
Returns
Return nil, nil when the check finds no problem; issues, nil for messages the author can act on; or an error when the check could not run. An error fails the feedback request. An issue with no Target belongs to the current field.
([]operation.Issue, error)How it works
Register this callback with LiveValidate. Register save rules separately with Validate: live feedback does not run during Save and cannot guarantee that a later save will succeed.
T matches the field’s write type: string for text-like fields and single choices; float64 for Number; bool for Checkbox; []string for MultiSelect; operation.ID or []operation.ID for relationships and uploads; store.Value for structured and plugin fields.
Ridu skips a callback when the field value cannot be decoded to its expected type. Missing or null input can still reach a callback as an empty operation.Value. Structured store.Value callbacks must check child values themselves; defaults, save hooks, and full validation have not run.
Keep the check repeatable and free of writes or other side effects. Pass ctx.Context to cancellable work and ctx.Local.FindByID; the request has a five-second deadline.
Full signature
type LiveValidator[T any] func(
operation.LiveValidationContext,
operation.Value[T],
) ([]operation.Issue, error)Related types
LiveValidationContextThe request and surrounding values available to a live server validation callback.
ValueValue carries an optional logical value. Its zero value is empty. For raw input, empty means omitted and Present(store.Null()) means explicit null. For typed scalars, empty is the portable empty state, not a durable absence encoding. T must follow its own ownership contract; this carrier does not deep-clone arbitrary mutable application values.
IssueIssue addresses the current field or a descendant relative to its candidate value. A zero Target selects the current field. Ridu supplies resource, field, locale, display-path and stable correlation information; callbacks never construct occurrence IDs.