Hi, Walter.
I faced with problem and not know how truly describe it.
I`m using react with ****.
NodeData Interface
key: number
category: 'Source'
header: {
text: string
styles: {
isBold: boolean
isCursive: boolean
isUnderLine: boolean
fontFamily: string
fontSize: number
fontColor: string
strokeColor: string
strokeWidth: number
isDotted: boolean
backgroundColor: string
width: number
height: number
}
}
settings: {
headerTextSize: string
bodyTextSize: string
}
adornmentList: Array<{
id: string
category: tAdornmentCategory,
styles: {
isBold: boolean
isCursive: boolean
isUnderLine: boolean
fontFamily: string
fontSize: number
fontColor: string
strokeColor: string
strokeWidth: number
isDotted: boolean
backgroundColor: string
width: number
height: number
alignment: string
},
value: string
}>
nodeTemplate
const BodyShapeSettings: Partial<go.Shape> = {
fill: 'white',
opacity: 1,
stroke: 'blue',
strokeWidth: 2,
strokeDashArray: [8, 4],
};
const BodyTextBlockSettings: Partial<go.TextBlock> = {
// editable: true,
cursor: 'move',
isMultiline: true,
overflow: go.TextBlock.OverflowEllipsis,
textAlign: 'center',
name: TEXT_BLOCK_BODY_NAME,
width: 100,
click: shapeClick,
};
const BodyPanelSettings: Partial<go.Panel> = {
// alignment: new go.Spot(0, 0, 0, 0),
margin: 0,
padding: 0,
};
const makeSourceBodyShape = () => new go.Shape('RoundedRectangle', BodyShapeSettings);
const makeSourceBody = () => new go.TextBlock('body_text', BodyTextBlockSettings)
.bind(makeTwoWayBinding('desiredSize', 'bodyTextSize', 'settings', go.Size.parse, go.Size.stringify));
const makeSourceBodyPanel = () => new go.Panel(go.Panel.Auto, BodyPanelSettings).add(makeSourceBodyShape()).add(makeSourceBody());
const HeaderShapeSettings: Partial<go.Shape> = {
};
const HeaderTextBlockSettings: Partial<go.TextBlock> = {
// editable: true,
cursor: 'move',
isMultiline: true,
overflow: go.TextBlock.OverflowEllipsis,
textAlign: 'center',
name: TEXT_BLOCK_HEADER_NAME,
width: 100,
click: shapeClick,
};
const HeaderPanelSettings: Partial<go.Panel> = {
margin: 0,
padding: 0,
};
const makeSourceHeaderShape = () => new go.Shape('RoundedRectangle', HeaderShapeSettings)
.bind(new go.Binding('stroke', 'header', (value: iSourceHeaderNodeData) => value.styles.strokeColor))
.bind(new go.Binding('strokeWidth', 'header', (value: iSourceHeaderNodeData) => value.styles.strokeWidth))
.bind(new go.Binding('fill', 'header', (value: iSourceHeaderNodeData) => value.styles.backgroundColor));
const makeSourceHeaderBody = () => new go.TextBlock(HeaderTextBlockSettings)
.bind(new go.Binding('stroke', 'header', (value: iSourceHeaderNodeData) => value.styles.fontColor))
.bind(new go.Binding('isUnderline', 'header', (value: iSourceHeaderNodeData) => value.styles.isUnderLine))
.bind(new go.Binding('text', 'header', (value: iSourceHeaderNodeData) => value.text))
.bind(new go.Binding('font', 'header', (value: iSourceHeaderNodeData) => renderFont(value.styles)))
.bind(makeTwoWayBinding('desiredSize', 'headerTextSize', 'settings', go.Size.parse, go.Size.stringify));
const makeSourceHeaderPanel = () => new go.Panel(go.Panel.Auto, HeaderTextBlockSettings)
.add(makeSourceHeaderShape())
.add(makeSourceHeaderBody());
const createSourceTemplate = () => $(
go.Node,
go.Panel.Vertical,
{
selectionObjectName: MAIN_SHAPE_NAME,
resizable: true,
resizeObjectName: TEXT_BLOCK_HEADER_NAME,
locationSpot: go.Spot.Center,
},
new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify),
$(
go.Panel,
go.Panel.Vertical,
makeSourceHeaderPanel(),
makeSourceBodyPanel(),
),
);
makeTwoWayBinding
const makeTwoWayBinding = (
targetName: string,
sourceName: string,
objectName: string,
conversationFn?: (value: string, target: string) => void,
backConversationFn?: typeof go.Spot.stringify | typeof go.Size.stringify | typeof go.Point.stringify | tBackConversationFn,
isUpdateNode?: boolean,
) => {
const bind = new go.Binding(targetName, objectName);
bind.mode = go.Binding.TwoWay;
bind.converter = (diagramSettings, target) => {
const value = diagramSettings[sourceName];
if (value === undefined) return target[targetName];
return (typeof conversationFn === 'function') ? conversationFn(value, target) : value;
};
bind.backConverter = (value, data, model) => {
const objectData = data[objectName];
if (model) {
model.setDataProperty(objectData, sourceName, (typeof backConversationFn === 'function')
? backConversationFn(value, objectData, model)
: value);
if (isUpdateNode) {
model.updateTargetBindings(data);
}
} else {
objectData[sourceName] = (typeof backConversationFn === 'function' ? backConversationFn(value, objectData, model) : value);
}
return objectData;
};
return bind;
};
My Reducers
const setNodeDataArray = (state: iDiagramState, action: PayloadAction<{ nodeDataArray: tDiagramObjectData[], isSkip?: boolean }>) => {
state.nodeDataArray = action.payload.nodeDataArray;
state.isSkipsDiagramUpdate = action.payload.isSkip || false;
};
const changeNodeStyle = (state: iDiagramState) => {
state.nodeDataArray.forEach((item) => {
item.header.styles.backgroundColor = 'brown';
return item;
});
state.isSkipsDiagramUpdate = false;
};
The changeNodeStyle change backgroundColor property in any nodes;
After dispatch changeNodeStyle backgroundColor successfully updates. But then when I try resize body or header panel I get next error.
If I change size body or header panel without update backgroundColor done without error.
If I change node location, the error not appear.
How I can solve it?
Error appear when I change properties in internal Objects such as header, body or settings
I read any examples from gojs docs.
In addition, attach code Diagram and modelUpdate function
const initSourceDiagram = () => {
const $ = go.GraphObject.make;
const diagram = $(go.Diagram, {
'undoManager.isEnabled': true, // must be set to allow for model change listening
model: $(go.GraphLinksModel, {
linkKeyProperty: 'key',
linkFromKeyProperty: 'from',
linkToKeyProperty: 'to',
copyNodeDataFunction: (original: go.ObjectData) => { // using only copy/past func
const newdata = { ...original };
return newdata;
},
}),
});
diagram.nodeTemplateMap.add(NodeCategories.Source, createSourceTemplate());
const getDiagram = () => diagram;
return {
getDiagram,
};
};
function handleModelChange(obj: go.IncrementalData) {
if (obj === null) return;
const { insertedNodeKeys } = obj;
const { modifiedNodeData } = obj;
const { removedNodeKeys } = obj;
// copy data to new array, but maintain references
let nodeArr = nodeDataArray.slice() as go.ObjectData[];
const modifiedNodeMap = new Map();
let arrChanged = false;
// handle node changes
if (modifiedNodeData) {
modifiedNodeData.forEach((nd) => {
modifiedNodeMap.set(nd.key, nd);
const idx = mapNodeKeyIdx.get(nd.key);
if (idx !== undefined && idx >= 0) {
nodeArr.splice(idx, 1, nd);
arrChanged = true;
}
});
}
if (insertedNodeKeys) {
insertedNodeKeys.forEach((key) => {
const nd = modifiedNodeMap.get(key);
const idx = mapNodeKeyIdx.get(key);
if (nd && idx === undefined) {
mapNodeKeyIdx.set(nd.key, nodeArr.length);
nodeArr.push(nd);
arrChanged = true;
}
});
}
if (removedNodeKeys) {
nodeArr = nodeArr.filter((nd) => {
if (removedNodeKeys.includes(nd.key)) {
arrChanged = true;
return false;
}
return true;
});
refreshNodeIndex(nodeArr);
}
if (arrChanged) {
updateDataModel({ nodeDataArray: nodeArr as typeof nodeDataArray, isSkip: true });
}
}