How to fix my OOP data Module?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

I am attempting to use OOP with my data module.

  1. What is the issue? Include screenshots / videos if possible!

It says that self.GlobalData is nil even though I spelled it correctly. I get an error at line 95.

here is the code:

local DataService = {}

local IndexTable = {
	__index = DataService
}

local MainStore = game:GetService("DataStoreService"):GetDataStore(script:WaitForChild("DataKey").Value)

local RunService = game:GetService("RunService")

local function GetDataType(data)
	if typeof(data) == "number" then
		return "IntValue"
	elseif typeof(data) == "string" then
		return "StringValue"
	elseif typeof(data) == "boolean" then
		return "BoolValue"
	end
end

function DataService.new(UserId)
	local newData = {}
	setmetatable(newData, IndexTable)
	
	local PlayerDataFolder = Instance.new("Folder")
	PlayerDataFolder.Parent = game:GetService("ServerStorage"):WaitForChild("GlobalData")
	PlayerDataFolder.Name = "Data-"..UserId
	
	local PlayerLocalFolder = Instance.new("Folder")
	PlayerLocalFolder.Parent =  game:GetService("ReplicatedStorage"):WaitForChild("LocalData")
	PlayerLocalFolder.Name = "Data-"..UserId	
	
	newData.PlayerId = UserId
	newData.Key = "Key-"..UserId
	newData.GlobalData = PlayerDataFolder
	newData.LocalData = PlayerLocalFolder
	
	game.Players.PlayerRemoving:Connect(function(player)
		if player.UserId == UserId then
			DataService:SaveData()
		end
	end)
	
	game:BindToClose(function()
		if RunService:IsStudio() then
			wait(3)
		else
			DataService:SaveData()
		end
	end)
	
	return newData
end

function DataService:CreateNewData(ValueName, DefaultValue)
	local ValueObjGlobal = Instance.new(GetDataType(DefaultValue))
	ValueObjGlobal.Parent = self.GlobalData
	ValueObjGlobal.Value = DefaultValue
	ValueObjGlobal.Name = ValueName
	
	local ValueObjLocal = Instance.new(GetDataType(DefaultValue))
	ValueObjLocal.Parent = self.LocalData
	ValueObjLocal.Value = ValueObjGlobal.Value
	ValueObjLocal.Name = ValueObjGlobal.Name
	
	ValueObjLocal.Value = ValueObjGlobal.Value
	ValueObjGlobal.Changed:Connect(function()
		ValueObjLocal.Value = ValueObjGlobal.Value
	end)
	
	local data
	
	local success, err = pcall(function()
		data = MainStore:GetAsync(self.Key)
		
		if data then
			for _,v in pairs(self.GlobalData:GetChildren()) do
				if data[v.Name] ~= nil then
					v.Value = data[v.Name]
				end
			end
		end
	end)
	
	if not success then
		print("error loading data: ")
		print(err)
	end
end

function DataService:SaveData()
	local SaveTable = {}

	for _,v in pairs(self.GlobalData:GetChildren()) do
		SaveTable[v.Name] = v.Value
	end
			
	for k,v in pairs(SaveTable) do
		if v == nil then
			table.remove(SaveTable, k)
		end
	end
	
	local success, err = pcall(function()
		MainStore:SetAsync(self.Key, SaveTable)
	end)
			
	if not success then
		print("error saving data")
		print(err)
	end
end

return DataService
3 Likes

Hi. I’m just learning OOP, but I think I’ve made this mistake many times. In game.Players.PlayerRemoving you must change this

DataService:SaveData()

for this one

newData:SaveData()

if you use DataService you are using the class, which has no properties.

3 Likes

Hi,

Not exactly sure of the context of this module, however I’ll assume.

When you are calling SaveData() do you want to save all the Datastores? (Datastores defined in DataService)

No thats not the issue, I would have to declare the functions inside the constructer which would be messy and destrys the point of the metatables

Well, I tried your module making the change I suggested and it no longer had the ‘self.GlobalData is nil’ error. Maybe you could give us more details about your issue?

1 Like

Maybe I dont get what your saying. Can you reply with you new code.

Since newData’s metatable is already set in the second line of the constructor function, you should be able to call DataService’s methods on newData without needing to define any more methods in the constructor. Even though newData has no functions of it’s own, the __index of it’s metatable does, so calling newData:SaveData() will have the same effect as DataService.SaveData(newData).

This is why @loyanafirst’s solution should work, as would my identical answer in the previous post.

of course. Inside the constructor you must change this

	game.Players.PlayerRemoving:Connect(function(player)
		if player.UserId == UserId then
			DataService:SaveData()
		end
	end)

for this one

	game.Players.PlayerRemoving:Connect(function(player)
		if player.UserId == UserId then
			newData:SaveData()  -- here
		end
	end)

the rest remains the same.

Calling the function is not the issue though. It the self.GlobalData not being recognized. If you could provide an example code with what your saying that would be cool so I am not misunderstanding your point

Ohhh I get what your saying. I just tested it and that seemed to solve the issue. Thanks to both fo you guys for helping but since @loyanafirst replied first I will have to mark hers as a solution. You all helped though. Thanks :grin: