Edit itemDataArray before links drawing

I want to add new links data properties (with computed value) in a linkDataArray before it was draw by the diagram. Is the only solution is to add a listener on the modelChangedListener and to modify each links after the end of the transaction ? Something like that (not tested) :

myDiagram.model = () => {
  return this.go(go.GraphLinksModel, {
     linkDataArray: [ { from: '1', to: '2' } ]
     ...
  });
}

myDiagram.addModelChangedListener((e) => {
    if (e.model instanceof go.GraphLinksModel &&  e.isTransactionFinished) {
        e.model.linkDataArray.forEach((link) => {
            e.model.setDataProperty(link, 'stroke', '#FF0000');
        });
    }
});

myDiagram.linkTemplate = () => {
  return this.go(go.Link, { 
      /* remove to simplify */ 
    },
    this.go(go.Shape, {
      /* remove to simplify */
    }),
    new go.Binding('stroke', 'stroke'),
  );
}

Is this correct ?

Certainly you can implement a “LinkDrawn” DiagramEvent listener that modifies the newly drawn Link (the e.subject) or its e.subject.data. See How to insertLink custom data for link while drawing link - #2 by walter

If you are asking about changing the data earlier, that is also possible – it depends on when.

For example, before a link is drawn by setting LinkingTool.archetypeLinkData: Add an extra property in the linkData during link creation

Or perhaps at LinkingTool.insertLink time: I want to catch drawing link event before the drawing has finished - #7 by walter

Raise only from a user action not from an initial linkDataArray.

But in the case, all data must be the same, I cant compute properties by link.

Same sounds like “LinkDrawn”, only with user action.

So my solution seem to be the unique way. Thanks walter.

Ah, I misunderstood your phrase “before links drawing”.

Also, your model and link template are functions, not instances of Model and Link, respectively.

This is why I use ES6 arrow function…

Walter do you have an event called only on the initial linkDataArray loading (not after an insert, delete or change property event) and before links are drawing ?

What specifically are you trying to achieve? There might be a better solution for you.

Imagein on the initial drawing, you have a nodeDataArray like this :

nodeDataArray = [
  { 
     key: 'A', 
     ports: [
         { key: 'A1', color: '#00FF00' },
         { key: 'A2, color: '#0000FF' }
     ]
 },
  { 
     key: 'B', 
     ports: [
         { key: 'B1', color: '#0000FF' },
         { key: 'B2, color: '#FFF000' }
     ]
  }
]

and this linkDataArray :

[
   { "from": "A", "to": "B", "fromPortId": "A1", "toPortId": "B1" }
   { "from": "A", "to": "B", "fromPortId": "A2", "toPortId": "B2" }
]

goJS create the two links from A1 to B1 and A2 to B2 with the default template. I need to change the brush using to draw the intials links with the colors sets in the dataNode (and not in the dataLink). You understand ?

There’s an example of this sort of behavior at GoJS Validation -- Northwoods Software.

All you need to do is something like:

new go.Binding("stroke", "fromPort", function(p) { return p.data.portColor; }).ofObject()

on the Link’s path Shape. For example, adding that Binding on the link template of the Dynamic Ports sample produces this:

It’s hard in that sample to tell about link direction, but that was intentional in that sample. I didn’t bother to add an arrowhead for this example of link path coloring.

Interesting. In my case (and I think it’s the normal behavior), a binding with fromPort as source, doesnt return a “data” structure. What do you have change to return the data of a port ?

In that Dynamic Ports sample, all of the ports are defined in itemArrays on the node data objects. Since each of the port data objects already had a “portColor” property, it seemed natural to use that.

If your data structures are different you need to adapt the code to your data. I can’t know what that might be. How are you assigning colors to the ports in your nodes?

Walter, I understood my problem. In my case and it’s a big difference with a basic example, properties fromLinkable and toLinkable are not set on the panel parent but directly on an inner shape (the circle). So when you try to get the data structure with a binding it refer to the shape and not the panel.

A little codepen example with a basic port on the right and a port template like mine on the left :

CodePen

Is this possible to let the fromLinkable / toLinkable on the sphape but retrieve data from his grand parent (panel contain a “pin” shape, “pin” contain the “circle” shape). I tried with part.data…but…

Then don’t look at the p.data..., but find the Shape that you care about and look at its Shape.fill.

Still, that doesn’t answer my question about how ports get different colors in the first place. I would have thought that that information would be in the data, but apparently not?

A this time. When a link is drawn or deleted I listen the “LinkDrawn”, “LinkRelinked” and “SelectionDeleted” where :

  • I get the current “status” for the both link ports in the nodeData structure (this property is dynamically define it doesn’t exist in the nodeDataArray)
  • I define a new status (ex. Connected) for each port
  • I get a color for the new status (ex. Connected = #00FF00)
  • I update with setDataProperty :

– the status and fill properties for the both ports in the nodeDataArray
– the fromColor / toColor properties for the link in the itemDataArray

The port template bind the fill property on the status data property (a function convert the value to an hex color).
The linkTemplate bind the fromColor / toColor to the same data properties.

That work’s perfectly for new, relinked or deleted links but I need to call this “method” for each link in the linkData on the initial loading (my initial question).

Ok it’s a solution BUT it still a problem… the link is created before the port status was updated, the link use not the correct color.

Well, you could make sure all of the data is updated in the model before you even assign Diagram.model.

Or you could update the links after the model has loaded, in an “InitialLayoutCompleted” DiagramEvent listener.

“InitialLayoutCompleted”, the whole diagram layout has updated for the first time since a major change to the Diagram, such as replacing the Model;

“major change”, this event can be called several times ?

Only once per replacement of Diagram.model, I believe.

Use “LayoutCompleted” if you want an event after each layout.

Ok thanks. Is it intentinonal you have removed “SelectionDeleting” from the API documentation ?

I don’t know – I think it should still be documented, even though the major reason for its usage is going away in version 2.0. We’ll investigate.

Ok walter. Just a last question

With :

$(go.Panel, 'Auto', new go.Binding('portId'));

We can retrieve model data bounded to this object, but with :

$(go.Panel, 'Auto',
   $(go.Shape, 'Rectangle', new go.Binding('portId'));
);

It’s not possible because a shape does not inherit from Panel. We can bind a shape but no get data binded… strange.

Do you have a workaround to simplify the access to this data instead of filter nodeData ?