Hi,
I am using four link templates. The selection of these templates is not a function of a single string property of the link data. Instead, I provide a function as the linkCategoryProperty
of the diagram’s model. I should say that I could accomplish this by creating an “empty string” binding on the entire link data object, but because we encounter large diagrams I have avoided such bindings altogether in the interest of efficiency. And indeed, the change of link category is driven by a user-initiated action, so I know exactly the moment that the category should be changed. And I call setCategoryForLinkData
at that moment.
Everything works as expected, except that the link is not redrawn on the spot. I have verified that
- when
setCategoryForLinkData
is called, it in turn calls the function bound tolinkCategoryProperty
with a single argument (the data) and is returned the correct category - refreshing the browser tab or forcing a reload of the diagram without a refresh (as though loading a new diagram from scratch) both draw the link correctly (per the new template)
- the link data is at it should be before and after
- all of the relevant model mutations and the call to
setCategoryForLinkData
are happening within a single (not nested) transaction.
The two data properties that determine a link’s category are src
and type
. The former is the linkFromPortIdProperty
for the model, and the latter is not associated with any bindings. The link category determines the link’s stroke
and strokeWidth
properties, among other things. The user actions causes the type
property of the data model to change. The src
data property is never mutated. Another wrinkle is that stroke
also has a binding (Object binding) to isHighlighted
. If it were not for that, it would be tempting to set stroke
on creation of the link and use a simple binding on type
to modify it over the lifetime of the link.
Any suggestions?
Here are a few snippets of the pertinent code:
makeEmptyDiagramModel()
{
let $ = go.GraphObject.make;
return $(go.GraphLinksModel,
{
copyNodeDataFunction: obj => {
// deep copy of the node's model data
let data = this.copyModelData(obj);
// delete the key so that copy/paste operations will provide a new one
delete data.key;
return data;
},
nodeCategoryProperty: "cat",
linkCategoryProperty: (data, cat) => { return this.linkTemplateCategory(data); },
linkFromPortIdProperty: "src",
linkToPortIdProperty: "dst",
linkKeyProperty: "key"
});
}
linkTemplateCategory(data)
{
if (this.isDynamicPort(data.src))
{
return (data.type && data.type != "?") ? "typedEW" : "untypedEW";
}
else
{
return (data.type && data.type != "?") ? "typedNS" : "untypedNS";
}
}
makeLinkTemplates(dict)
{
let map = new go.Map();
map.add("untypedEW", this.makeLinkTemplate("lightslategray", 2, 1));
map.add("untypedNS", this.makeLinkTemplate("darkslategray", 1, 2));
map.add("typedEW", this.makeLinkTemplate("blue", 2, 1));
map.add("typedNS", this.makeLinkTemplate("midnightblue", 1, 2));
return map;
}
bindEdgeTypeCmd(menuItem) { this.askEdgeType(type => { this.doEdgeType(menuItem, "bindEdgeType", type); }); }
askEdgeType(success)
{
let validate = text => { return this.WS.test(text) ? "?" : this.validateJSONType(text); };
let failure = () => { return "invalid type"; };
let cancel = () => {};
this.prompt("edge type:", "", validate, success, failure, cancel);
}
doEdgeType(menuItem, f, type)
{
let diagram = this.findActiveDiagram();
let selection = diagram.selection;
if (this.canEdgeTypeSelection(selection))
{
diagram.startTransaction("EdgeType");
this[f](selection, type);
diagram.commitTransaction("EdgeType");
this.signalMenu(menuItem, true);
}
else
{
this.signalMenu(menuItem, false);
}
}
canEdgeTypeSelection(selection)
{
let k = 0;
selection.each(part => {
if (part instanceof go.Link)
{
++ k;
}
});
return k > 0;
}
bindEdgeType(selection, type)
{
let model = this.findActiveModel();
selection.each(part => {
if (part instanceof go.Link)
{
let data = part.data;
// propagate this type to the ports at both ends of the edge
this.setLinkType(model, part, type);
// record edge types that are explicitly set by the user
model.setDataProperty(data, "type", type);
// determine the new category of the edge as a fn of data
let newcategory = this.linkTemplateCategory(data);
// ** update the link category (should force it to be redrawn)
model.setCategoryForLinkData(data, newcategory);
}
});
}
The line following the // **
is the one I was expecting would cause the link to be recreated and redrawn.