Why is my data module not finding a self variable

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 trying to make my data module have a save function.

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

I run the save function and I get the error “attempt to index nil with getchildren” on line 91 but when I check the code the self variable in the save function it has the exact name I set it as in the DataService.new()

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I have tried many things but cannot find any information on my issue.

here is the class:

local DataService = {}
DataService.__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 PlayerData = {}
	setmetatable(PlayerData, DataService)
	
	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	
	
	PlayerData.PlayerId = UserId
	PlayerData.Key = "Key-"..UserId
	PlayerData.GlobalData = PlayerDataFolder
	PlayerData.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 PlayerData
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
1 Like

In the constructor you define the new object as ‘PlayerData’, so maybe in the parts where ‘DataService’ methods are used in the constructor try using PlayerData:SaveData() instead of DataService:SaveData()

That way you would be calling the method on the created object, not the metatable.

1 Like

I will have to delcare them inside the constructer then and that would be very messy

1 Like

so i actually just tested it with switcing out the constructer variables insde from PlayerData. to DataService. and it works fine but that is not the solution i am looking for as I used the the other way in my previous game and it worked fine

1 Like

Since DataService is being set as PlayerData’s metatable, with itself as it’s __index, calling :SaveData() on PlayerData will invoke that method in DataService, while passing in the instance of PlayerData as the ‘self’ parameter.