I want GoJS to set Locations of Nodes automatic if TwoWay Binding has no data

I am developing an application where 2 scenarios are present.

First is standard mode, where user drags nodes from pallette and creates links to connect nodes.
Second is auto mode, where certain questions will be asked to user, and based on his inputs a diagram will be created automatically. I mean nodeDataArray and linkDataArray will be created programatically.

To handle standard scenario, i am using following two way binding. because i may need to bind diagram based on the location stored in database.

new go.Binding("location", "Location", go.Point.parse).makeTwoWay(go.Point.stringify)

Now in Auto Mode, when i am adding nodeDataArray Location is set to null value by default. So all of my nodes are shown in center of diagram. Overlapping each other.

image

What is Expected ?
I expect goJS to set Location as per it own, such that they dont overlap on each other.

In that case you could just call myDiagram.layoutDiagram(true). Since you have a TwoWay Binding on Node.location, your model data will automatically be updated with the locations determined by the Diagram.layout.

You’ll want that Layout to have Layout.isInitial set to false, and maybe Layout.isOngoing to false too, so that when you load an existing model the layout won’t be performed, wiping away all of the saved locations in the model. Read more about layout invalidation at: GoJS Layouts -- Northwoods Software

I tried to add both of the isInital & isOngoing to false.

Also added this.diagram.layoutDiagram(true), but nothing changes. is there any specific instruction to use this. I mean before data loads, or after model loaded something like this ?

If you set both Layout.isInitial and isOngoing to false, then the layout will never happen unless your code calls Diagram.layoutDiagram.

So right after you load a new model by replacing Diagram.model, you can decide whether to call layoutDiagram. Clearly if the model data doesn’t have all of its node data have location information, you’ll want to call layoutDiagram.

I added following 2 lines in ngOnInit where my all templates and everything is being setup.

What i expected is, my diagram will ignore all locations and it will not be drawn. But as a result, there was no change in my any of the diagram.

Another thing i tried is after the model is associated to diagram, i called layoutDiagram(true). But as a result there was no change in any of the diagram.

What might be going wrong ? let me know if you want to have a look at model or template .

I just tried this Diagram initialization in a minimal sample:

            layout: $(go.TreeLayout, { isInitial: false, isOngoing: false }),
            "InitialLayoutCompleted": function(e) {
              if (!e.diagram.nodes.all(n => n.location.isReal())) {
                e.diagram.layoutDiagram(true);
              }
            },

My node template has a Binding on Node.location.

When I load a model (i.e. create a model and assign it to Diagram.model) that does not have valid location information on each node data, it automatically performs the TreeLayout.

When the model data do all include location information, then the layout does not happen and the nodes are all located where the data specified.

As i am using Angular 5, my architecture demands a slight different mechanism. So what i have done here is, as soon as a model is set, i am calling 1 method which checks whether all node’s lcoations have same value 0 or not.

if (this.diagram.nodes.all(x => x.location.x == 0 && x.location.y == 0)) {
            var allnodes = this.diagram.nodes.iterator;
            while (allnodes.next()) {
                var n = allnodes.value;
                n.location.x = null;
                n.location.y = null;

            }

            this.diagram.layout = go.GraphObject.make(go.TreeLayout, { isInitial: false, isOngoing: false });
            this.diagram.layoutDiagram(true);

Still it is not working, It still renders all of the nodes in center. all together overlapping each others.

image

what might be going wrong still ? any way to identify /debug this ?

Are you using go-debug.js while you are developing, as we recommend? I think it might catch the error of trying to set Point.x or Point.y to null.

Furthermore, it is also illegal to modify the value of Part.location’s x or y properties. You would need to replace the Part.location value with a new Point.

Also, TreeLayout will assign the location of every Node that it lays out, so your trying to set the location beforehand will have no effect.

I am using npm gojs module. Not sure it is debug one or regular one.

I think it might catch the error of trying to set Point.x or Point.y to null. - I cant see any error being logged in console, i was also debugging in chrome inspector when this operation was taking place. It did not threw any error. earlier when i tried to set location property directly. It threw some exception which was catched and logged in console.

new go.Binding("location", "Location", go.Point.parse).makeTwoWay(go.Point.stringify)

when my Location property has value null, i dont know why node’s location property has X =0 and Y =0. Is it default value for all nodes when it can not parse from Location ?

I changed position of TreeLayout to initialization part. Still not working.

Following is my complete NodeTemplate. anything wrong with it ?

return $(
                go.Node, go.Panel.Auto, { padding: new go.Margin(2), movable: true }, new go.Binding("movable", "AllowMove"), new go.Binding("location", "Location", go.Point.parse).makeTwoWay(go.Point.stringify), { contextMenu: myContextMenu },
                $(go.Shape, "Rectangle", new go.Binding("stroke", "isHighlighted",
                    function (h) { return h ? "red" : "black"; }).ofObject(), { strokeWidth: 0, fill: "transparent" }, new go.Binding("strokeWidth", "strokeWidth"), new go.Binding("strokeWidth", "isHighlighted", function (h) { return h ? 2 : 0; })
                        .ofObject()),
                $(go.Panel, go.Panel.Auto, { defaultAlignment: go.Spot.Left },
                    $(go.Panel, go.Panel.Vertical, { padding: new go.Margin(2), width: 70, defaultAlignment: go.Spot.Center },
                        $(go.Picture, { maxSize: new go.Size(32, 32), margin: 2 }, new go.Binding("source", "ImageURI")),
                        $(go.TextBlock, new go.Binding("text", "FullName").makeTwoWay(), { editable: true, margin: 2, font: "12px 'Open Sans', sans-serif", textAlign: "center" }))),
                {
                    dragComputation: stayInGroup,
                    selectionAdornmentTemplate: $(go.Adornment, "Auto", $(go.Shape, "Rectangle", { fill: null, stroke: "green", strokeWidth: 2 }), $(go.Placeholder)),
                    fromLinkableSelfNode: false, toLinkableSelfNode: false
                },
                MakePort("LC", go.Spot.LeftCenter, true, true),
                MakePort("RC", go.Spot.RightCenter, true, true),
                MakePort("TC", go.Spot.TopCenter, true, true),
                MakePort("BC", go.Spot.BottomCenter, true, true),
                MakePort("BL", go.Spot.BottomLeft, true, true),
                MakePort("BR", go.Spot.BottomRight, true, true),
                MakePort("TL", go.Spot.TopLeft, true, true),
                MakePort("TR", go.Spot.TopRight, true, true),
                MakePort("C", go.Spot.Center, true, true)
            );

The default value for Part.location is (NaN, NaN), as documented: Part | GoJS API

I really think you would benefit from using go-debug.js instead of go.js. Just try changing the import statement. We can’t help you if you don’t catch these simple errors on your own.

I changed to gojs-debug.js, and i found that my this.diagram.layoutDiagram(true) was not inside the transaction which was the reason that it was not updating on server.

So i added it inside the transaction and i can see that it updated all locations on my server.

Key Type PositionX PositionY NodeName
1 Group 1 23.0841796875 iPhone
2 Group 12 46.168359375 Software
3 Group 89 207.7576171875 Hardware
4 Group 23 69.2525390625 File System
5 Group 34 92.33671875 User Partition(Encrypted)
6 Group 45 115.4208984375 App Sandbox 1
7 Node 110 230.841796875 Data Protection Class
8 Node 110 230.841796875 My App
9 Group 100 230.841796875 OS Partition
10 Node 110 230.841796875 iOS
11 Group 100 230.841796875 Services
12 Node 110 230.841796875 Contacts
13 Node 110 230.841796875 Location
14 Node 110 230.841796875 Phone
15 Node 110 230.841796875 Storage
16 Node 110 230.841796875 SMS
17 Group 100 230.841796875 Ports
18 Node 110 230.841796875 NFC
19 Node 110 230.841796875 Bluetooth
20 Node 110 230.841796875 WiFi
21 Group 100 230.841796875 User Interaction
22 Node 110 230.841796875 Capacitive Touch Sensor
23 Node 110 230.841796875 Fingerprint Scanner
24 Node 110 230.841796875 Microphone
25 Node 110 230.841796875 Camera
26 Node 110 230.841796875 Accelerometer
27 Group 110 230.841796875 Keychain
8 Node 0 0 My App
29 Group 100 230.841796875 Kernel
30 Node 110 230.841796875 Secure Element
31 Node 110 230.841796875 Secure Enclave

but i found that it generated same X and Y for all nodes inside a Group.

Any hint for this ?

What is your Group.layout?

No layout specified in Group template.

Did they start with those locations in the database? If you don’t set Group.layout to an instance of a layout class that inherits from Layout, the forced layout by calling Diagram.layoutDiagram won’t move any nodes that are within a group.

No, initially all the locations are NULL whether its a Group or Node inside a Group. when Diagram.layoutDiagram is called, its location is set.

As Diagram.layoutDiagram has set some proper location values to all Groups, similarly i want to have different location for all nodes inside a Group.

can you suggest some Group.layout which produce desired output ?

Depends on what you want to achieve. I’d start with GridLayout.