Documentation
¶
Overview ¶
Package localentity declares records that live in the visitor's browser and renders the forms and lists that edit them, with no server table and no JavaScript in the app.
var Team = localdb.New("team")
var Members = localentity.Define(Team, "members", []schema.Field{
{Name: "name", Type: schema.String, Required: true, Max: ptr(20)},
{Name: "level", Type: schema.Int, Min: ptr(1), Max: ptr(100)},
}, localentity.Indexed("level"), localentity.MaxRecords(6))
A declared entity is a core-ui/localdb store with three built-in fields: id (a UUIDv7 string minted in the browser), created_at and updated_at (RFC 3339 strings). Its forms and lists render through the design system on the server, like every other page; the localentity-form behaviour then saves form submissions into IndexedDB, and the localentity behaviour fills each list by cloning its server-rendered row template, setting text only. Every tab of the site re-renders when any tab writes.
Nothing here talks to the server after the page loads, and the server never sees the records. A later sync layer can carry them to a real database; the declarations use core/schema field types so the same fields can describe a server entity.
Index ¶
Constants ¶
const ( FieldID = "id" FieldCreatedAt = "created_at" FieldUpdatedAt = "updated_at" )
The built-in fields every local entity carries.
Variables ¶
var DefaultMessages = Messages{
Required: "This field is required.",
Number: "Enter a number.",
Integer: "Enter a whole number.",
Min: "Must be at least {n}.",
Max: "Must be at most {n}.",
MinLen: "Must be at least {n} characters.",
MaxLen: "Must be at most {n} characters.",
Pattern: "Use the requested format.",
Choice: "Choose one of the listed options.",
Date: "Enter a valid date.",
Full: "This list is full ({n} at most).",
Failed: "Could not save on this device.",
}
DefaultMessages are the English defaults.
Functions ¶
This section is empty.
Types ¶
type Entity ¶
type Entity struct {
// contains filtered or unexported fields
}
Entity is one declared local entity.
func Define ¶
Define declares a local entity named name in db, with the given fields, and returns it. It declares the backing store itself (keyed by id, created_at and updated_at indexed, plus Indexed fields), so name must not already be a store in db. Field names are lowercase snake_case ([a-z][a-z0-9_]*, at most 61 bytes, so "by_<name>" is a valid index name), not "constructor" or "prototype", and must not reuse a built-in. An Indexed field must be Required. Every refusal is a panic at startup.
func (*Entity) Count ¶
Count renders a span the behaviour fills with how many records the entity holds, kept current across tabs.
func (*Entity) Form ¶
Form wraps a design-system form (ui.Form) so its submissions are saved into this entity instead of posted. Each control's name is a field name; controls with other names (the CSRF input, a button) are ignored. id names the carrier, for Row.Edit. The browser's own constraint validation runs first; the behaviour then coerces and checks each value against the field declarations and places any refusal beside its field.
ui.Form still needs an Action: without JavaScript the form posts there natively, so point it at a route that explains the page needs script, or at the page itself.
func (*Entity) List ¶
func (e *Entity) List(cfg ListConfig) List
List returns a list over the entity's records.
type List ¶
type List struct {
// contains filtered or unexported fields
}
List is a configured list of an entity's records. Compose it inside a layout component: the rows become that component's children.
l := Members.List(localentity.ListConfig{OrderBy: "level", Desc: true})
ui.Grid(ui.GridConfig{Min: "14rem"}, l.Render(
l.Row(func(r localentity.Row) render.HTML { return ui.Card(...) }),
l.Empty(ui.EmptyState(...)),
))
func (List) Empty ¶
Empty renders what the list shows when it has no records. One root element, like Row.
func (List) Render ¶
Render returns the list carrier holding its row and empty templates. Until the behaviour fills it, it shows nothing: the server cannot see the visitor's records.
type ListConfig ¶
type ListConfig struct {
// OrderBy is the field rows are ordered by: created_at (the
// default), updated_at, or a field declared Indexed. IndexedDB
// does the ordering through the index; page script never sorts.
OrderBy string
// Desc reverses the order (newest first, highest first).
Desc bool
// Limit caps how many rows render. 0 renders every record.
Limit int
}
ListConfig configures a List.
type Messages ¶
type Messages struct {
Required string `json:"required"`
Number string `json:"number"`
Integer string `json:"integer"`
Min string `json:"min"`
Max string `json:"max"`
MinLen string `json:"minlen"`
MaxLen string `json:"maxlen"`
Pattern string `json:"pattern"`
Choice string `json:"choice"`
Date string `json:"date"`
Full string `json:"full"`
Failed string `json:"failed"`
}
Messages are the words the behaviour places beside a field (or toasts, for Full and Failed) when a submission is refused. The browser's own validation runs first on any control the form renders with required/min/max/maxlength/pattern; these cover the rest and a form rendered without them.
type Option ¶
type Option func(*options)
Option configures Define.
func Indexed ¶
Indexed adds an IndexedDB index over each named field, so a list can order by it (ListConfig.OrderBy). created_at and updated_at are always indexed. A Bool field cannot be indexed: IndexedDB keys are never booleans.
func MaxRecords ¶
MaxRecords caps how many records the entity holds. A create past the cap is refused with Messages.Full and nothing is written; edits still work. 0 means no cap.
func WithMessages ¶
WithMessages replaces the validation messages the behaviour shows. Empty fields keep their defaults. {n} is replaced by the bound.
type Row ¶
type Row struct {
// contains filtered or unexported fields
}
Row marks where a record's values go inside a row template.
func (Row) Delete ¶
Delete wraps a control (a ui.Button with Type "button") so a click deletes this row's record.