Browse all documentation

Virtual field

Calculate a read-only value in Go, such as a display name from first and last names.

Use field.Virtual to calculate a value when a document is read. For example, combine first and last names into a display name. The value appears in API responses and generated output types, but is not stored and cannot be submitted when creating or updating a document.

In the admin

A read-only Computed label virtual field in the Ridu admin showing a value resolved by Go code.

The admin displays the calculated value as read-only.

Calculate a display name

content/people.go
package content

import (
  "github.com/riducms/ridu"
  "github.com/riducms/ridu/field"
  "github.com/riducms/ridu/operation"
)

var People = ridu.Collection{
  Slug: "people",
  Fields: field.Fields{
    field.Text("firstName").Required(),
    field.Text("lastName").Required(),
    // The resolver returns a string, so displayName is a text value.
    field.Virtual("displayName",
      func(
        ctx operation.Context,
      ) (operation.Value[string], error) {
        first, _ := ctx.Root.String("firstName")
        last, _ := ctx.Root.String("lastName")
        return operation.Present(first + " " + last), nil
      }),
  },
}

Read sibling values

Virtual fields are root fields, so their sibling values live in ctx.Root. In the example, displayName reads the stored firstName and lastName siblings with ctx.Root.String(...). The second return value reports whether the value is actually a string; the example can ignore it because both source fields are required Text fields.

Read other fields with ctx.Root.Get("name"). On the returned value, call BooleanValue() for a checkbox or NumberValue() for a number. Read a Group or named tab’s children with Get, and iterate an Array or Blocks list with Elements(). These reads share immutable values without copying the containers. A Relationship or Upload contains its saved ID or reference. Use ctx.Local to read the related document with the current user’s access rules.

Virtual fields must live at a collection or global root. Resolvers receive the operation, actor and auth collection, current document, locale selection, context, and access-controlled Local API. Use that Local API for additional reads.

Choose the value type

The resolver’s return type sets the field’s type in API responses and generated types:

Resolver returns Response value
operation.Value[string] Text
operation.Value[float64] A number
operation.Value[bool] true or false
operation.Value[store.Value] Any JSON value, such as an object

Return operation.Present(value), or operation.Empty[T]() for null. The compiler checks that a scalar resolver returns its declared type. A resolver is required; a missing one fails startup.

Configuration

Constructor or method What it controls
field.Virtual(name, resolver) Declares the response key and the Go callback that calculates it; its type sets the value type.
.Access(...) / .RestrictAccess(...) Controls whether the computed output is visible to the caller.
.AfterRead(...) Transforms the resolved response value before final redaction.
.Admin(...) Sets label, description, position, width, and read-only presentation.

Virtual has no default, requiredness, write validator, write hook, localization setting, or stored column. .AfterRead transforms receive and return the resolver’s type, such as operation.Value[string].

Avoid unnecessary work when reading lists

Ridu skips a virtual field when it is not selected in the read. If a calculation is expensive, request it only on the screens that need it. Be especially careful with list reads: a network request for every row can make the whole list slow. Cache repeated work where appropriate and handle failures. Field read access rules still apply to the calculated value.

Common mistakes

  • Do not try to filter or sort by a value that is never stored. Save the calculated value in a stored field when the database needs to filter or sort it.
  • Avoid nondeterministic or secret-bearing output unless an access rule permits it.
  • A Virtual is not an admin-only UI element; use UI for presentation with no response value.

See field.Virtual, field.VirtualField, and field.Resolver.