#Values
event declares a named value and binds a callback to it.
local Interface = Scope:Capture({
ClassName = "TextLabel",
mount(PlayerGui),
event("Count", 0, function(self, _, Value)
self.Text = `Clicks: {Value}`
end),
})
Interface:Mount()
Interface.Count(5) -- label now reads "Clicks: 5"
The name becomes a field on the Vision. Call it with nothing to read, with a value to write. A write returns the value, so it composes:
local Next = Interface.Count(Interface.Count() + 1)
#Staged writes coalesce
Before mounting, a write only stores:
Interface.Count(1)
Interface.Count(2)
Interface.Count(3)
-- nothing has run
Interface:Mount()
-- the callback runs once, with 3
#Unchanged writes are skipped
After mounting, writing the value it already holds does nothing at all:
Interface.Mode("active") -- callbacks run
Interface.Mode("active") -- skipped
Which means you can write freely from a loop or a network handler without checking first.
Warning
This makes writes idempotent. If you were relying on re-writing the same value to re-run a callback, it will not fire.
#One value, many instances
This is where it gets useful. merge binds another callback to a value
declared elsewhere:
{
ClassName = "Frame",
mount(PlayerGui),
{
ClassName = "TextLabel",
Name = "Percent",
event("Fill", 0.2, function(self, _, Value)
self.Text = string.format("%.0f%%", Value * 100)
end),
},
{
ClassName = "Frame",
Name = "Bar",
merge("Fill", function(self, _, Value)
self.Size = UDim2.fromScale(Value, 1)
end),
},
}
One write updates both, and each callback gets its own self:
Interface.Fill(0.75)
Declaring the same name with event twice does the same thing as merge -
the second declaration adds a binding rather than replacing the value. The
first InitialValue wins.
#Next
Lifecycle - running code at the right time.