Problem with OOP returning attempt to call a nil value

Hello, members of the DevFourm. I’m having a problem with my script which uses OOP.

I want to fix my attempt to index nil with error in OOP.

I’m having an issue with what I think is settings __index which is causing the attempt to index nil with error. Correct me if my troubleshooting is wrong.

Solutions I’ve tried is asking AI for help and searching the DevFourm. I changed the 11th line to HorizontalTabBar.__index = HorizontalTabBarbut received another error of attempt to call a nil value

The script below is a script which attempts to make a tab bar which you can add tabs to and it will automatically resize. self._SetupTabHandling() and self._SetupMouseClickHandling() are the parts of the script which error. These functions handle how self._tabButton look and behave.

----------------------------------------
--
-- HorizontalTabBar.lua
--
-- Creates a horizontal tab bar with a set amount of tabs.
--
----------------------------------------
local GuiUtilities = require(script.Parent.GuiUtilities)

local HorizontalTabBar = {}
HorizontalTabBar.__index = HorizontalTabBar

function HorizontalTabBar.new(suffix: string)
	local self = {}
	setmetatable(self, HorizontalTabBar)

	local titlebar = Instance.new("Frame")
	titlebar.Name = "TabBar" .. suffix
	titlebar.BorderSizePixel = 0
	titlebar.Size = UDim2.new(1, 0, 0, 29)
	titlebar.Position = UDim2.new(0, 0, 0, 0)
	GuiUtilities.syncGuiTabBarBackgroundColor(titlebar)
	self._titlebar = titlebar

	local frame = Instance.new("Frame")
	frame.Name = "TabFrame" .. suffix
	frame.BorderSizePixel = 0
	frame.BackgroundTransparency = 0
	frame.Size = UDim2.new(1, 0, 1, -30)
	frame.Position = UDim2.new(0, 0, 0, 30)
	GuiUtilities.syncGuiElementBackgroundColor(frame)
	self._frame = frame

	local uiPageLayout = Instance.new("UIPageLayout")
	uiPageLayout.Name = "TabBarPageLayout" .. suffix
	uiPageLayout.EasingStyle = Enum.EasingStyle.Cubic
	uiPageLayout.TweenTime = 0.2
	uiPageLayout.Parent = frame
	self._uiPageLayout = uiPageLayout

	local uiListLayout = Instance.new("UIListLayout")
	uiListLayout.Name = "TabBarListLayout" .. suffix
	uiListLayout.FillDirection = Enum.FillDirection.Horizontal
	uiListLayout.SortOrder = Enum.SortOrder.LayoutOrder
	uiListLayout.Padding = UDim.new(0, 2)
	uiListLayout.Parent = titlebar
	self._uiListLayout = uiListLayout

	self._titlebar.ChildAdded:Connect(function()
		local tabButtonWidth = 1 / (#self._titlebar:GetChildren() - 1)
		for _, child in pairs(self._titlebar:GetChildren()) do
			if child:IsA("TextButton") then
				child.Size = UDim2.new(tabButtonWidth, 0, 0, 27)
			end
		end
	end)
	self._titlebar.ChildRemoved:Connect(function()
		local tabButtonWidth = 1 / (#self._titlebar:GetChildren() - 1)
		for _, child in pairs(self._titlebar:GetChildren()) do
			if child:IsA("TextButton") then
				child.Size = UDim2.new(tabButtonWidth, 0, 0, 27)
			end
		end
	end)

	self._selected = false
	self._hovered = false

	self._SetupMouseClickHandling()

	return self
end

function HorizontalTabBar:_SetupMouseClickHandling()
	self._tabButton.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			self._hovered = true
			self:_updateCheckboxVisual()
		end
	end)

	self._tabButton.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			self._hovered = false
			self:_updateCheckboxVisual()
		end
	end)

	self._tabButton.MouseButton1Down:Connect(function()
		self._selected = true
		self:_updateCheckboxVisual()
	end)
end

function HorizontalTabBar:_UpdateCheckboxVisual()
	local kButtonDefaultBackgroundColor =
		settings().Studio.Theme:GetColor(Enum.StudioStyleGuideColor.RibbonTab, Enum.StudioStyleGuideModifier.Default)
	local kButtonHoverBackgroundColor =
		settings().Studio.Theme:GetColor(Enum.StudioStyleGuideColor.RibbonTab, Enum.StudioStyleGuideModifier.Hover)
	local kButtonSelectedBackgroundColor =
		settings().Studio.Theme:GetColor(Enum.StudioStyleGuideColor.RibbonTab, Enum.StudioStyleGuideModifier.Selected)

	if self._selected then
		self._tabButton.BackgroundColor3 = kButtonSelectedBackgroundColor
	elseif self._hovered then
		self._tabButton.BackgroundColor3 = kButtonHoverBackgroundColor
	else
		self._tabButton.BackgroundColor3 = kButtonDefaultBackgroundColor
	end
end

function HorizontalTabBar:AddTab(suffix, name)
	local tabButton = Instance.new("TextButton")
	tabButton.AutoButtonColor = true
	tabButton.Name = "TabButton" .. suffix
	tabButton.BorderSizePixel = 2
	tabButton.Position = UDim2.new(0, 0, 0, 0)
	tabButton.Size = UDim2.new(1, 0, 0, 27)
	tabButton.Font = GuiUtilities.kDefaultFontFace
	tabButton.TextSize = GuiUtilities.kDefaultFontSize
	tabButton.Text = name
	tabButton.Parent = self._titlebar
	-- GuiUtilities.syncGuiElementFontColor(tabButton)
	-- GuiUtilities.syncGuiElementTitlebarColor(tabButton)
	-- GuiUtilities.syncGuiElementBorderColor(tabButton)

	tabButton.MouseButton1Down:Connect(function()
		self._uiPageLayout:JumpTo(self:GetContentsFrame(suffix))
	end)
end

function HorizontalTabBar:GetTitlebar()
	return self._titlebar
end

function HorizontalTabBar:GetContentsFrame(suffix)
	return self._frame:FindFirstChild("VerticalScrollFrame" .. suffix)
end

function HorizontalTabBar:GetFrame()
	return self._frame
end

return HorizontalTabBar

You define the class methods with colon but call them with a dot. Using a colon implicitly passes self as an argument but when you call it with a dot it’s not passed so self becomes nil. You’ll need to change how you call the functions.

In your constructor:

- self._SetupMouseClickHandling()
+ self:_SetupMouseClickHandling()

I didn’t see any case of SetupTabHandling besides you mentioning it in the thread’s content body but I assume it’s the same issue of missing that colon has an implicit first argument.

1 Like

I’m already calling with a dot? Below is how I called the class.

local tabBar = HorizontalTabBar.new("Tabs")

My point is that you shouldn’t be on anything besides your constructor. You define everything except new with colon but call them in turn with dots, meaning the function calls aren’t picking up the implicit self argument from the use of colon syntax to call methods.

local foobar = {}

function foobar:garply()
end

-- These are the same:
foobar:garply()
foobar.garply(foobar)

You have the second case but without the passed argument.

1 Like

Also, a nitpick. This is not necessary, here’s a shorthand:

local self = setmetatable(self, HorizontalTabBar)

setmetatable returns the table that was set, so you don’t have to.

What should I do next? I’m only using the dot for one function.

function HorizontalTabBar.new(suffix: string)

The rest uses colons.

Please take a look at my reply again. It has the details.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.