From e79457a95293a984000ff8b2eadb2d716b01d584 Mon Sep 17 00:00:00 2001 From: Ankitkumar Satapara Date: Mon, 20 Apr 2026 19:17:13 +0530 Subject: [PATCH] Fixed foreach loop connection input and output types --- .../Nodify.Calculator/CalculatorViewModel.cs | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/Examples/Nodify.Calculator/CalculatorViewModel.cs b/Examples/Nodify.Calculator/CalculatorViewModel.cs index 4d723c1..49dfb82 100644 --- a/Examples/Nodify.Calculator/CalculatorViewModel.cs +++ b/Examples/Nodify.Calculator/CalculatorViewModel.cs @@ -39,6 +39,9 @@ namespace Nodify.Calculator // Dynamic Take node: adapt connectors when a list connects if (!IsLoading) HandleTakeNodeConnected(c); + + // Dynamic ForEach node: adapt Current Item output based on list type + if (!IsLoading) HandleForEachNodeConnected(c); }) .WhenRemoved(c => { @@ -68,6 +71,9 @@ namespace Nodify.Calculator // Dynamic Take node: reset on disconnect HandleTakeNodeDisconnected(c); + + // Dynamic ForEach node: reset Current Item output on disconnect + HandleForEachNodeDisconnected(c); }); Operations.WhenAdded(x => @@ -636,6 +642,115 @@ namespace Nodify.Calculator takeOp.Output.Remove(o); }); } + + private void HandleForEachNodeConnected(ConnectionViewModel c) + { + // Find the ForEach node's Grid (List) input connector + ConnectorViewModel forEachListInput = null; + ConnectorViewModel sourceConnector = null; + + if (c.Input.Shape == ConnectorShape.Grid && c.Input.IsInput && c.Input.Operation is ForEachOperationViewModel) + { + forEachListInput = c.Input; + sourceConnector = c.Output; + } + else if (c.Output.Shape == ConnectorShape.Grid && c.Output.IsInput && c.Output.Operation is ForEachOperationViewModel) + { + forEachListInput = c.Output; + sourceConnector = c.Input; + } + + if (forEachListInput == null || sourceConnector == null) return; + + var forEachOp = (ForEachOperationViewModel)forEachListInput.Operation; + + // Determine the element type from the source connector's DataType + var dataType = sourceConnector.DataType ?? ""; + string elementType; + bool isList = dataType.StartsWith("List<") && dataType.EndsWith(">"); + if (isList) + elementType = dataType.Substring(5, dataType.Length - 6); + else + elementType = dataType; + + // Check if the element type is a model (custom class) or a primitive + var primitiveTypes = new[] { "string", "int", "double", "bool", "float", "decimal", "long", "object", "" }; + bool isModel = !primitiveTypes.Contains(elementType.ToLower()); + + // Remove existing "Current Item" output (non-triangle, non-Index) + var currentItemOutputs = forEachOp.Output + .Where(o => o.Shape != ConnectorShape.Triangle && o.Title != "Index") + .ToList(); + foreach (var o in currentItemOutputs) + { + DisconnectConnector(o); + forEachOp.Output.Remove(o); + } + + if (isModel) + { + // Model type: Square connector so it can connect to Split nodes + forEachOp.Output.Add(new ConnectorViewModel + { + Title = elementType, + IsInput = false, + Shape = ConnectorShape.Square, + ConnectorColor = System.Drawing.Color.MediumPurple, + DataType = elementType + }); + } + else + { + // Primitive type: Circle connector with type-appropriate color + var color = ConnectorViewModel.GetColorForType(elementType); + forEachOp.Output.Add(new ConnectorViewModel + { + Title = string.IsNullOrEmpty(elementType) ? "Current Item" : $"Item ({elementType})", + IsInput = false, + Shape = ConnectorShape.Circle, + ConnectorColor = color, + DataType = elementType + }); + } + } + + private void HandleForEachNodeDisconnected(ConnectionViewModel c) + { + ConnectorViewModel forEachListInput = null; + + if (c.Input.Shape == ConnectorShape.Grid && c.Input.IsInput && c.Input.Operation is ForEachOperationViewModel) + forEachListInput = c.Input; + else if (c.Output.Shape == ConnectorShape.Grid && c.Output.IsInput && c.Output.Operation is ForEachOperationViewModel) + forEachListInput = c.Output; + + if (forEachListInput == null) return; + + var forEachOp = (ForEachOperationViewModel)forEachListInput.Operation; + + // Check if input still has a connection + var stillConnected = Connections.Any(con => con.Input == forEachListInput || con.Output == forEachListInput); + if (stillConnected) return; + + // Remove existing "Current Item" output (non-triangle, non-Index) + var currentItemOutputs = forEachOp.Output + .Where(o => o.Shape != ConnectorShape.Triangle && o.Title != "Index") + .ToList(); + foreach (var o in currentItemOutputs) + { + DisconnectConnector(o); + forEachOp.Output.Remove(o); + } + + // Reset to default Circle "Current Item" output + forEachOp.Output.Add(new ConnectorViewModel + { + Title = "Current Item", + IsInput = false, + Shape = ConnectorShape.Circle, + ConnectorColor = System.Drawing.Color.MediumSpringGreen, + DataType = "object" + }); + } } }