Data Binding
Data binding creates reactive UIs that automatically update when data changes.
What is Data Binding?
Data binding connects widgets to data sources. When the data changes, the widget updates automatically without manual SetText() calls.
Binding Types
String Binding
require("@gui")
let name = binding.NewString("John")
let label = widget.NewLabelWithData(name)
// Update data - label updates automatically
name.Set("Jane")
// Read current value
let current = name.Get()Bool Binding
let enabled = binding.NewBool(false)
let checkbox = widget.NewCheckWithData("Enable feature", enabled)
// Listen for changes
enabled.AddListener(() => {
print("Enabled:", enabled.Get())
})
// Toggle
enabled.Set(!enabled.Get())Int Binding
let count = binding.NewInt(0)
let label = widget.NewLabelWithData(
count.Map(x => sprintf("Count: %d", x))
)
// Increment
count.Set(count.Get() + 1)Float Binding
let temperature = binding.NewFloat(20.5)
let slider = widget.NewSliderWithData(0, 100, temperature)
let label = widget.NewLabelWithData(
temperature.Map(x => sprintf("%.1f°C", x))
)
// Slider changes automatically update label
Binding Methods
All binding types support these methods:
.Get()- Retrieve current value.Set(value)- Update value (triggers UI refresh).AddListener(callback)- Execute callback on change.Map(fn)- Transform value for display
Transform with Map
Use .Map() to format binding values:
let price = binding.NewFloat(19.99)
let label = widget.NewLabelWithData(
price.Map(x => sprintf("$%.2f", x))
)
let percent = binding.NewFloat(0.75)
let display = widget.NewLabelWithData(
percent.Map(x => sprintf("%.0f%%", x * 100))
)Complete Example: Counter App
require("@gui")
let count = binding.NewInt(0)
let countLabel = widget.NewLabelWithData(
count.Map(x => sprintf("Count: %d", x))
)
let incrementBtn = widget.NewButton("Increment", () => {
count.Set(count.Get() + 1)
})
let decrementBtn = widget.NewButton("Decrement", () => {
count.Set(count.Get() - 1)
})
let resetBtn = widget.NewButton("Reset", () => {
count.Set(0)
})
window.SetContent(container.NewVBox(
countLabel,
container.NewHBox(incrementBtn, decrementBtn, resetBtn)
))Two-Way Binding
Widgets with data binding automatically sync in both directions:
let text = binding.NewString("")
let entry = widget.NewEntryWithData(text)
let label = widget.NewLabelWithData(text)
// Typing in entry automatically updates label
// Programmatic changes update both
text.Set("Hello") // Updates entry AND label
Multiple Listeners
Attach multiple callbacks to one binding:
let value = binding.NewInt(0)
value.AddListener(() => {
print("Value changed to:", value.Get())
})
value.AddListener(() => {
window.SetStatus(sprintf("Count: %d", value.Get()))
})
value.Set(5) // Triggers both listeners
Form with Binding
require("@gui")
let name = binding.NewString("")
let email = binding.NewString("")
let subscribe = binding.NewBool(false)
let form = container.NewVBox(
widget.NewFormItem("Name:", widget.NewEntryWithData(name)),
widget.NewFormItem("Email:", widget.NewEntryWithData(email)),
widget.NewCheckWithData("Subscribe", subscribe),
widget.NewButton("Submit", () => {
print("Name:", name.Get())
print("Email:", email.Get())
print("Subscribe:", subscribe.Get())
})
)
window.SetContent(form)When to Use Binding
Use data binding when:
- Multiple widgets display the same data
- UI should update automatically on data change
- Building reactive forms
- Synchronizing related widgets
Use direct methods when:
- One-time widget updates
- Simple static UIs
- Performance-critical tight loops
Summary
Data binding provides:
- Automatic UI updates
- Two-way synchronization
- Clean separation of data and UI
- Multiple listener support
- Value transformation with
.Map()
Available types: String, Bool, Int, Float