UCloud logo UCloud logo UCloud
v2025.1.0
  1. UCloud/Core
  2. 1. Introduction
  3. 2. Projects
  4. 3. Accounting
  5. 4. Orchestration
  6. UCloud/IM for Slurm-based HPC
  7. 5. Installation
  8. 6. Architecture and Networking
  9. 7. User and Project Management
  10. 8. Filesystem Integration
    1. 8.1. Inter-provider file transfers
  11. 9. Slurm Integration
    1. 9.1. Application Management
    2. 9.2. Built-in Applications
  12. 10. Reference
    1. 10.1. Configuration
    2. 10.2. CLI
  13. 11. Appendix
    1. 11.1. Built-in Application Index
  14. UCloud/IM for Kubernetes
  15. 12. Installation
  16. 13. Architecture and Networking
  17. 14. Filesystem Integration
  18. 15. Compute Jobs
    1. 15.1. Public Links
    2. 15.2. Public IPs
    3. 15.3. License Servers
    4. 15.4. SSH Servers
  19. 16. Integrated applications
    1. 16.1. Syncthing
    2. 16.2. Integrated terminal
  20. 17. UCX applications
    1. 17.1. Hello world
    2. 17.2. Data binding
    3. 17.3. UI events
    4. 17.4. Component reference
    5. 17.5. API reference
  21. 18. Reference
    1. 18.1. Configuration
    2. 18.2. CLI
  22. H: Procedures
  23. 19. H: Procedures
  24. 20. H: Introduction
  25. 21. H: Auditing
  26. 22. H: Auditing scenario
  27. 23. H: GitHub actions
  28. 24. H: Deployment
  29. 25. H: 3rd party dependencies (risk assesment)
  1. Links
  2. Source Code
  3. Releases

Data binding

UCX binds frontend input to exported Go fields using model paths.

Field mapping rules

By default, exported field names are mapped to lower camel case:

  • JobName -> jobName
  • ValidationMessage -> validationMessage

You can override this with ucx tags:

type appModel struct {
    JobName string
    Errors  map[string]string

    // Excluded from model serialization
    NextTodoId int64 `ucx:"-"`

    // Custom key in model
    StackName string `ucx:"stack.id"`
}

Binding inputs and output

Bind paths point into your serialized model:

ucx.InputText("jobName", "Job name", "Name your job", "jobName")
ucx.TextBound("errors.jobName")
ucx.TextBound("validationMessage")

For list rendering, use relative paths inside row templates:

ucx.List("todos", "No items yet.").Children(
    ucx.TextBoundEx("todoItemText", "./text"),
    ucx.ButtonEx("removeTodo", "Remove", ucx.ColorErrorMain, ucx.IconHeroTrash, "", "./id"),
)

./text and ./id resolve relative to the current list item.

Handling model input

UCX sends model edits as OpModelInput. In most apps you:

  1. normalize model values,
  2. run validation,
  3. clear stale status messages.
func (app *myApp) OnMessage(msg ucx.Frame) {
    switch msg.Opcode {
    case ucx.OpModelInput:
        app.JobName = strings.TrimSpace(app.JobName)
        app.Errors = validateState(app)
        app.SubmissionMessage = ""
    }
}

ucx.AppServe(...) already applies model input into your struct (ApplyModelInput) before OnMessage(...) is called.

Validation pattern

A simple and effective approach is map[string]string for errors:

func validateState(app *myApp) map[string]string {
    errors := map[string]string{}
    if len(strings.TrimSpace(app.JobName)) < 3 {
        errors["jobName"] = "Job name must be at least 3 characters"
    }
    if app.CPU < 1 || app.CPU > 128 {
        errors["cpu"] = "CPU must be between 1 and 128"
    }
    return errors
}

Then render with TextBound("errors.jobName"), TextBound("errors.cpu"), etc.

Previous Hello world
Next UI events