Looping through a table with ipairs doesn't update

Hi,

I have a loop that goes through a table of buttons to detect whenever one of them is clicked. But the problem is the table is not updating so whenever I add a new button to the table it doesn’t do anything.

for i, button in ipairs(trunkButtons) do
		button.MouseButton1Click:Connect(function()
			moveToInventoryEvent:FireServer(button.toolItem.Value)
		end)
	end

just do while true do

so it will check again when loop ends

while true do
     for i, button in ipairs(trunkButtons) do
		button.MouseButton1Click:Connect(function()
			moveToInventoryEvent:FireServer(button.toolItem.Value)
		end)
	end
wait()
end

Thanks, but as far as I know it won’t run any script after that and mine looks like this:

openGuiEvent.OnClientEvent:Connect(function(trunkItems, plrItems)
	
	-- open gui --
	frame.Visible = true
	local frame = script.Parent
	loadAllItems(plrItems, trunkItems)
	
	for i, button in ipairs(trunkButtons) do
		button.MouseButton1Click:Connect(function()
			moveToInventoryEvent:FireServer(button.toolItem.Value)
		end)
	end
	
	for i, button in ipairs(inventoryButtons) do
		button.MouseButton1Click:Connect(function()
			moveToTrunkEvent:FireServer(button.toolItem.Value)
		end)
	end
	
end)

It has to loop through to tables :frowning:

how do you update the table can i see the script of the table

When you are adding new button update the table / Add it into the table (if you havent done it already)

The loop that connects the event for the initial buttons can continue to be used, however, a ChildAdded event can be used alongside this so that any new GuiButtons that are added into the “trunkButtons” and “inventoryButtons” activate the intended function/RemoteEvent.

In this case, we’ll create a new function so that the event connection can be created in a singular place (adhering by the “DRY”/Don’t Repeat Yourself principle) rather than duplicating the button.MouseButton1Click:Connect(function() to each loop/function.

local function buttonEventConnection(button, buttonType)
    if button:IsA("GuiButton") and buttonType == "trunk" then
        button.MouseButton1Click:Connect(function()
            moveToTrunkEvent:FireServer(button.toolItem.Value)
        end)

    elseif button:IsA("GuiButton") and buttonType == "inventory"
        button.MouseButton1Click:Connect(function()
            moveToInventoryEvent:FireServer(button.toolItem.Value)
        end)

    end
end

Afterwards, the loops can be modified to activate that function for each button so that the event can be connected:

for i, button in ipairs(trunkButtons) do
    buttonEventConnection(button, "trunk")
end
	
for i, button in ipairs(inventoryButtons) do
    buttonEventConnection(button, "inventory")
end

Then, the same thing can be applied for the new items that are added into the “trunkButtons” and “inventoryButtons”

trunkButtons.ChildAdded:Connect(function(item)
    buttonEventConnection(item, "trunk")
end)

inventoryButtons.ChildAdded:Connect(function(item)
    buttonEventConnection(item, "inventory")
end)

Afterward, here’s what everything looks like!

openGuiEvent.OnClientEvent:Connect(function(trunkItems, plrItems)
	
	-- open gui --
	frame.Visible = true
	local frame = script.Parent
	loadAllItems(plrItems, trunkItems)
	

    --- New Code Below ---


    local function buttonEventConnection(button, buttonType)
        if button:IsA("GuiButton") and buttonType == "trunk" then
            button.MouseButton1Click:Connect(function()
                moveToTrunkEvent:FireServer(button.toolItem.Value)
            end)

        elseif button:IsA("GuiButton") and buttonType == "inventory"
            button.MouseButton1Click:Connect(function()
                moveToInventoryEvent:FireServer(button.toolItem.Value)
            end)

        end
    end


    for i, button in ipairs(trunkButtons) do
        buttonEventConnection(button, "trunk")
    end
	
    for i, button in ipairs(inventoryButtons) do
        buttonEventConnection(button, "inventory")
    end

    trunkButtons.ChildAdded:Connect(function(item)
        buttonEventConnection(item, "trunk")
    end)

    inventoryButtons.ChildAdded:Connect(function(item)
        buttonEventConnection(item, "inventory")
    end)

end)
1 Like

I am adding it into the table, I am using table.insert(table, button)

Thank you so much for a detailed explanation, I will try this right now and tell you if I experience any problems!

1 Like

This line seems to return an error, attempt to index nil with Connect

trunkButtons.ChildAdded:Connect(function(item)
1 Like

Since nobody spoke up about this, this is a terrible solution. You’re going to be constantly creating event connections and leaking memory. Not trying to be rude, I just don’t want a random Googler showing up and thinking “oh, it works, I’ll use this”. I recommend you edit your post.

As others have said in this thread, the best thing to do is to connect up an event to a function which connects the events on the new object appropriately.

1 Like

It happens as soon as I open the gui, when the gui event is received it connects into a function that loads plr items and trunk items which look like this:

local function loadAllItems(plrItems, trunkItems)
	
	-- clear --
	
	-- plr --
	for i, button in ipairs(plrInventory:GetChildren()) do
		if button:IsA("TextButton") then
			button:Destroy()
		end
	end
	
	-- trunk --
	for i, button in ipairs(trunkInventory:GetChildren()) do
		if button:IsA("TextButton") then
			button:Destroy()
		end
	end
	
	-- load --
	
	-- plr --
	for i, item in ipairs(plrItems) do
		if item:IsA("Tool") then	
			local itemButton = itemTemplate:Clone()
			itemButton.toolItem.Value = item
			itemButton.Text = item.Name
			itemButton.Name = item.Name
			itemButton.Parent = plrInventory
			table.insert(inventoryButtons, itemButton)
		end
	end
	
	-- trunk --
	for i, item in ipairs(trunkItems) do
		if item:IsA("ObjectValue") then	
			local itemButton = itemTemplate:Clone()
			itemButton.toolItem.Value = item.Value
			itemButton.Text = item.Name
			itemButton.Name = item.Name
			itemButton.Parent = trunkInventory
			table.insert(trunkButtons, itemButton)
		end
	end
	
end
1 Like

Ah, are “trunkButtons” and “inventoryButtons” tables? Since the place where those variables were initialized/first assigned a value wasn’t included in your second post, I presumed that those were Instances (such as a frame or a folder) which contained the buttons.

If so, the error occurs because the ChildAdded event is intended for Instances – in order to detect when tables are updated, you would need to use metamethods if I recall correctly. Otherwise, the code may need to be restructured to have “trunkButtons” and “inventoryButtons” refer to the Instance(s) that contains all of those buttons.

Oh :frowning: , I really hoped it’s gonna be easier but ok. Btw do you have an idea how would I store it in a instance?

Before continuing, I have to go do something in a few minutes so I may not be able to respond for a few hours.


Are the buttons for the “trunk” and “inventory” scattered throughout various sections of a ScreenGui or are they consolidated into a single container. Essentially, are all of the buttons for the Inventory on the first layer (or subsequent layers) of a frame for the inventory?

Trunk buttons are in one frame and inventory buttons in another. All I do is when they are clicked the switched from inventory to trunk or from trunk to inventory.

In that case, if you change the “trunkButtons” and “inventoryButtons” variable to reference those frames (e.g. trunkButtons = ScreenGui.frame), the example code I provided above should work as intended since the ChildAdded event would be able to be connected to each frame.

Can you provide how “trunkButtons” and “inventoryButtons” are collected?

Hey,
I did some changes and my code now looks like this:

openGuiEvent.OnClientEvent:Connect(function(trunkItems, plrItems)
	
	-- open gui --
	frame.Visible = true
	local frame = script.Parent
	loadAllItems(plrItems, trunkItems)

	for i, button in ipairs(trunkInventory:GetChildren()) do
		if button:IsA("TextButton") then
			button.MouseButton1Click:Connect(function()
				moveToInventoryEvent:FireServer(button.toolItem.Value)
			end)
		end
	end

	for i, button in ipairs(plrInventory:GetChildren()) do
		if button:IsA("TextButton") then
			button.MouseButton1Click:Connect(function()
				moveToTrunkEvent:FireServer(button.toolItem.Value)
			end)
		end
	end
	
end)

moveToInventoryPassedEvent.OnClientEvent:Connect(function(itemTool)

	for i,v in ipairs(trunkInventory:GetChildren()) do
		if v:IsA("TextButton") and v.Name == itemTool.Name then
			v.Parent = plrInventory
		end
	end
	
	for i,v in ipairs(trunkInventory:GetChildren()) do
		if v:IsA("TextButton") and v.Name == itemTool.Name then
			v:Destroy()
		end
	end
	
end)

moveToTrunkPassedEvent.OnClientEvent:Connect(function(itemTool)
	
	for i,v in ipairs(plrInventory:GetChildren()) do
		if v:IsA("TextButton") and v.Name == itemTool.Name then
			v.Parent = trunkInventory
		end
	end

	for i,v in ipairs(plrInventory:GetChildren()) do
		if v:IsA("TextButton") and v.Name == itemTool.Name then
			v:Destroy()
		end
	end
	
end)

It works nearly perfectly but the problem is that when I open the gui and move stuff from inventory to trunk then I cant move trunk items back to inventory without holding E on proximity prompt again (opening the gui), then I can move trunk items into inventory but again cant move inventory items to trunk without reopening the gui. 99% sure it’s about updating but I don’t get how, it’s not a table it’s frame:GetChildren(), I tried what you’ve sent me before but it doesn’t work either :frowning:

To clarify, is the “openGuiEvent” function run when the ProximityPrompt is activated?

There’s the possibility that it isn’t working as intended still because the new items that are added into both the “trunkInventory” and “plrInventory” are not accounted for since the loop will only connect the events for each button that was there at the time the loop was run.

Did anything appear in the Output when you were testing that? The variable names might not have matched up properly since I based my example code on the original names of “trunkButtons” and “inventoryButtons” whereas the most recent code you sent has them renamed to “trunkInventory” and “plrInventory”. Although it’s likely that you accounted for this when testing to see if it worked or not, I wanted to mention it just in case.


After viewing the newest code you provided and based on my understanding of how the system you’ve created is structured, with your new setup that references an Instance to access the buttons, the code that was provided in my original reply should ensure that any new buttons which have been transferred between the “trunk” and “inventory” have the MouseButton1Click event connected properly as a result of the ChildAdded events that are connected to the “trunkInventory” and “plrInventory” containers.

Code example from my original reply (with updated variable names)
openGuiEvent.OnClientEvent:Connect(function(trunkItems, plrItems)
	
	-- open gui --
	frame.Visible = true
	local frame = script.Parent
	loadAllItems(plrItems, trunkItems)
	

    --- New Code Below ---


    local function buttonEventConnection(button, buttonType)
        if button:IsA("GuiButton") and buttonType == "trunk" then
            button.MouseButton1Click:Connect(function()
                moveToTrunkEvent:FireServer(button.toolItem.Value)
            end)

        elseif button:IsA("GuiButton") and buttonType == "inventory"
            button.MouseButton1Click:Connect(function()
                moveToInventoryEvent:FireServer(button.toolItem.Value)
            end)

        end
    end


    for i, button in ipairs(trunkInventory) do
        buttonEventConnection(button, "trunk")
    end
	
    for i, button in ipairs(plrInventory) do
        buttonEventConnection(button, "inventory")
    end

    trunkInventory.ChildAdded:Connect(function(item)
        buttonEventConnection(item, "trunk")
    end)

    plrInventory.ChildAdded:Connect(function(item)
        buttonEventConnection(item, "inventory")
    end)

end)

Extra Note

Also, I’d like to point out that you included duplicate loops during the function that was activated from the moveToInventoryPassedEvent which doesn’t appear to be required in order to achieve the intended effect.

For reference, this:

for i,v in ipairs(trunkInventory:GetChildren()) do
    if v:IsA("TextButton") and v.Name == itemTool.Name then
        v.Parent = plrInventory
    end
end
	
for i,v in ipairs(trunkInventory:GetChildren()) do
    if v:IsA("TextButton") and v.Name == itemTool.Name then
        v:Destroy()
    end
end

Could be consolidated to this (and the same would apply for the loops within the “moveToTrunkPassedEvent”):

for i,v in ipairs(trunkInventory:GetChildren()) do
    if v:IsA("TextButton") and v.Name == itemTool.Name then
        v.Parent = plrInventory
        task.defer(v.Destroy, v) -- From my understanding, this will ensure that the Instance is not destroyed immediately, lessening the likelihood that certain errors which occur upon the immediate deletion of an Instance could occur. The main one that came to mind was the "parent property locked" error but that usually happens when the Parent property of an Instance is set and that same Instance has :Destroy() called on it.
    end
end

Reference: Task Library Example

Hi,
Yes the event is fired when proximity prompt is triggered. I will try this after I come back home from school. And thank you for the extra notes i dont know how i didnt see that lol.