AI assistance used for creation of this issue
Description
Updating a node property from null to an object value using nodes.update() causes a TypeError in the selectiveNotDeepExtend, blocking updates when node properties are set to null.
Steps to Reproduce
- Create a network with a node that has a property set to
null.
- Attempt to update that property to an object value using
nodes.update().
Minimal Reproduction
import { DataSet, Network } from "vis-network/standalone";
const container = document.getElementById("mynetwork");
var nodes = new DataSet([
{ id: 1, label: "Node 1", test: null },
{ id: 2, label: "Node 2" },
{ id: 3, label: "Node 3" },
{ id: 4, label: "Node 4" },
{ id: 5, label: "Node 5" },
]);
var edges = new DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 },
{ from: 3, to: 3 },
]);
const networkData = {
nodes: nodes,
edges: edges
}
const options = {}
const network = new Network(container, networkData, options);
nodes.update({
id: 1,
test: { x: 1, y: 2 }
})
Expected Behavior
The node updates successfully. The test property changes from null to { x: 1, y: 2 }.
Actual Behavior
The following error is thrown:
Uncaught TypeError: can't access property "constructor", a[prop] is null
selectiveNotDeepExtend vis-network_standalone.js:7470
parseOptions Node.js:344
setOptions Node.js:170
update NodesHandler.js:331
update NodesHandler.js:29
_trigger vis-network_standalone.js:14570
_trigger vis-network_standalone.js:14569
update vis-network_standalone.js:15042
<anonymous> error.js:30
vis-network_standalone.js:7470:11
Root Cause
The selectiveNotDeepExtend function attempts to check if a property is an Object by accessing its constructor property:
function selectiveNotDeepExtend(propsToExclude, a, b) {
// ...
for (const prop in b) {
// ...
if (b[prop] && b[prop].constructor === Object) {
if (a[prop] === void 0) {
a[prop] = {};
}
// ERROR OCCURS HERE: a[prop] is null, but code tries to access a[prop].constructor
if (a[prop].constructor === Object) {
deepExtend(a[prop], b[prop]);
} else {
copyOrDelete(a, b, prop, allowDeletion);
}
}
// ...
}
return a;
}
Only checks if a[prop] is undefined, not null. If a[prop] is null, a[prop].constructor === Object fails.
Workarounds
- Remove and add the node again.
- Get the node with
nodes.get(id), mutate it directly, then call nodes.update() separately.
- Avoid having
null properties on nodes entirely.
Environment
- vis-network version: 10.0.2 [standalone version]
Suggested Fix
Update the selectiveNotDeepExtend function to verify that a[prop] is neither null nor undefined before accessing its constructor property. For example:
if (b[prop] && b[prop].constructor === Object) {
if (a[prop] === void 0) {
a[prop] = {};
}
// Add null check here
if (a[prop] !== null && a[prop].constructor === Object) {
deepExtend(a[prop], b[prop]);
} else {
copyOrDelete(a, b, prop, allowDeletion);
}
}
Alternatively, use optional chaining to safely check the constructor:
if (b[prop] && b[prop].constructor === Object) {
if (a[prop] === void 0) {
a[prop] = {};
}
if (a[prop]?.constructor === Object) {
deepExtend(a[prop], b[prop]);
} else {
copyOrDelete(a, b, prop, allowDeletion);
}
}
AI assistance used for creation of this issue
Description
Updating a node property from
nullto anobjectvalue usingnodes.update()causes a TypeError in theselectiveNotDeepExtend, blocking updates when node properties are set to null.Steps to Reproduce
null.nodes.update().Minimal Reproduction
Expected Behavior
The node updates successfully. The
testproperty changes fromnullto{ x: 1, y: 2 }.Actual Behavior
The following error is thrown:
Root Cause
The
selectiveNotDeepExtendfunction attempts to check if a property is an Object by accessing itsconstructorproperty:Only checks if
a[prop]isundefined, notnull. Ifa[prop]isnull,a[prop].constructor === Objectfails.Workarounds
nodes.get(id), mutate it directly, then callnodes.update()separately.nullproperties on nodes entirely.Environment
Suggested Fix
Update the
selectiveNotDeepExtendfunction to verify thata[prop]is neithernullnorundefinedbefore accessing itsconstructorproperty. For example:Alternatively, use optional chaining to safely check the constructor: