PropertyModule - Easily detect changes to variables

Introducting… properties module! Detect changes to values without using Value objects!

Big thanks to @Scriptos for helping me out with this. He originally modified Aero Game Framework to do this very thing, but in a style that fits aero. So now, I am creating this module so everyone else can use it.

Every once and a while, I’ll see people asking how to detect changes to a value. It’s not easy to do, and I’ve seen some people even recommending while loops to do this. You could use an Object value, or you could use PropertyModule!

Example usage:

local rs = game:GetService("ReplicatedStorage");
local PropertiesModule = require(rs:WaitForChild("PropertyModule"));

local Values = PropertiesModule.new();
Values.Enabled = false; -- Will not fire, as it is set before the event is created.

Values:GetPropertyChangedEvent("Enabled"):Connect(function(New, Old)
	print(Values.Enabled, Old);
end)

Values.Enabled = true; -- Will fire the event!
Values:Set("Enabled", false) -- Set Values.Enabled to false without firing the event.
Values:End(); -- No need for events.

Timer Example

local rs = game:GetService("ReplicatedStorage");
local PropertiesModule = require(rs:WaitForChild("PropertyModule"));

local Values = PropertiesModule.new();
Values.Timer = 0;

Values:GetPropertyChangedEvent("Timer"):Connect(function(New, Old)
	if Values.Timer > 0 then
		wait(1);
		Values.Timer = Values.Timer - 1;
		print(Values.Timer);
	elseif Values.Timer == 0 then
		print("All done!");
	end
end)

Values.Timer = 5; -- All you need to do to start the timer!

(no, Enabled has nothing to do with the Module itself. Enabled is just a property, it can be called anything)

You are able to call GetPropertyChangedEvent before the variable is even defined! So the very first time you set the variable, it will fire the event.

This does not use Value objects. It is done with metatables.

Documentation

Summary

ModuleScript
PropertiesModule.new()

  • returns a PropertiesModule class

Class
Method Set(PropertyName, NewValue)

  • Set a property without firing the associated event.

Method End()

  • All events are disconnected. You can still access and set properties.

Method GetPropertyChangedEvent(PropertyName)

  • Returns an event that passes the new, and old value.

Get it here!
https://www.roblox.com/library/5024176989/PropertyModule

Module compatible with Aero Game Framework
(once you have these modules setup, everything you need is already a member of self! self:RegisterProperty, self:GetPropertyChangedEvent, and you set registered variables though self too! self.SomeVariable = SomeValue. Pretty nifty, I’d say…)

Property Shared Module
-- Properties
-- WorldPosition
-- May 13, 2020

--[[
	RegisterProperty(Name)
	
	GetPropertyChangedEvent(Name):Connect(function(New, Old))
--]]


local Properties = {}

function Properties:Init()
	local Event = self.Shared.Event;
	local Aero = getmetatable(self).__index;
	
	
	function Aero:RegisterProperty(PropertyName)
		if not self.Properties then
			self.Properties = {};
			self.PropertyEvents = {};
		end
		self.Properties[PropertyName] = false;
		self.PropertyEvents[PropertyName] = Event.new();
		return self.PropertyEvents[PropertyName];
	end
	
	function Aero:GetPropertyChangedEvent(PropertyName)
		local evnt = nil;
		if not self.Properties or self.PropertyEvents[PropertyName] == nil then
			evnt = self:RegisterProperty(PropertyName);
		end
		if evnt == nil then
			evnt = self.PropertyEvents[PropertyName];
		end
		return evnt;
	end
	
	getmetatable(self).__index = function(self, index)
		if rawget(self, "Properties") and rawget(self.Properties, index) ~= nil then
			return rawget(self.Properties, index);
		else
			if rawget(Aero, index) ~= nil then
				return Aero[index];
			end
		end
	end
	
	getmetatable(self).__newindex = function(self, key, value)
		if rawget(self, "Properties") and rawget(self.Properties, key) ~= nil then
			local old = self.Properties[key];
			rawset(self.Properties, key, value)
			self.PropertyEvents[key]:Fire(value, old);
		else
			rawset(self, key, value);
		end
	end
	
end

return Properties
PropertyController
-- Property Controller
-- WorldPosition
-- May 14, 2020

--[[
	

--]]

local Properties;

local PropertyController = {}
PropertyController.__aeroOrder = 1;


function PropertyController:Init()
	Properties = self.Shared.Properties;
end


return PropertyController
PropertyService
-- Property Service
-- WorldPosition
-- May 14, 2020

--[[
				

--]]

local Properties;

local PropertyService = {Client = {}}
PropertyService.__aeroOrder = 1;


function PropertyService:Init()
	Properties = self.Shared.Properties;
end


return PropertyService
35 Likes

This concept sounds sick! Always wondered if this was possible to do. Will definitely try this :grin:

1 Like

This is honestly really cool, ill use it in my current projects, tysm

1 Like

Unfortunately, I can only set GetPropertyChangedEvent() once for each variable, is that right?