CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-gojs

Interactive diagrams, charts, and graphs library for creating flowcharts, org charts, UML, BPMN, and hundreds of other diagram types with rich layouts and tools.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

data-models.mddocs/

Data Models

GoJS uses a model-driven architecture where diagram content is defined by data models that are automatically synchronized with the visual diagram through data binding. Models provide change tracking, undo/redo support, and JSON serialization.

Capabilities

Model (Abstract Base)

Base class for all diagram data models, providing core data management and change tracking functionality.

abstract class Model {
  /**
   * Creates a new Model.
   * @param init - Optional initialization properties
   */
  constructor(init?: Partial<Model>);
  
  // Data Arrays
  nodeDataArray: ObjectData[];
  
  // Configuration
  dataFormat: string;
  nodeKeyProperty: string;
  nodeCategoryProperty: string;
  copyNodeDataFunction: ((data: ObjectData, model: Model) => ObjectData) | null;
  
  // Validation
  nodeFilter: ((data: ObjectData) => boolean) | null;
  
  // Change Events
  addChangedListener(listener: (e: ChangedEvent) => void): void;
  removeChangedListener(listener: (e: ChangedEvent) => void): void;
  
  // Node Data Management
  addNodeData(obj: ObjectData): void;
  removeNodeData(obj: ObjectData): void;
  containsNodeData(obj: ObjectData): boolean;
  findNodeDataForKey(key: any): ObjectData | null;
  getKeyForNodeData(data: ObjectData): any;
  getCategoryForNodeData(data: ObjectData): string;
  
  // Property Management
  setDataProperty(obj: ObjectData, propname: string, val: any): void;
  getDataProperty(obj: ObjectData, propname: string): any;
  
  // Serialization
  toJson(): string;
  toIncrementalJson(e: ChangedEvent): string;
  fromJson(str: string): void;
  fromIncrementalJson(str: string): void;
  
  // Transactions
  startTransaction(tname?: string): boolean;
  commitTransaction(tname?: string): boolean;
  rollbackTransaction(): boolean;
  
  // State
  isReadOnly: boolean;
  skipsUndoManager: boolean;
  undoManager: UndoManager | null;
}

GraphLinksModel extends Model

Model for diagrams with nodes and separate link objects, supporting complex many-to-many relationships.

class GraphLinksModel extends Model {
  /**
   * Creates a new GraphLinksModel.
   * @param nodeDataArray - Array of node data objects
   * @param linkDataArray - Array of link data objects
   * @param init - Optional initialization properties
   */
  constructor(
    nodeDataArray?: ObjectData[], 
    linkDataArray?: ObjectData[], 
    init?: Partial<GraphLinksModel>
  );
  
  // Link Data Management
  linkDataArray: ObjectData[];
  linkKeyProperty: string;
  linkFromKeyProperty: string;
  linkToKeyProperty: string;
  linkFromPortIdProperty: string;
  linkToPortIdProperty: string;
  linkCategoryProperty: string;
  linkLabelKeysProperty: string;
  
  // Link Operations
  addLinkData(obj: ObjectData): void;
  removeLinkData(obj: ObjectData): void;
  containsLinkData(obj: ObjectData): boolean;
  findLinkDataForKey(key: any): ObjectData | null;
  getKeyForLinkData(data: ObjectData): any;
  getFromKeyForLinkData(data: ObjectData): any;
  getToKeyForLinkData(data: ObjectData): any;
  getFromPortIdForLinkData(data: ObjectData): string;
  getToPortIdForLinkData(data: ObjectData): string;
  getCategoryForLinkData(data: ObjectData): string;
  
  // Link Queries
  findLinksForNode(nodedata: ObjectData): ObjectData[];
  findLinksInto(nodedata: ObjectData): ObjectData[];
  findLinksOutOf(nodedata: ObjectData): ObjectData[];
  
  // Validation
  linkFilter: ((data: ObjectData) => boolean) | null;
  copyLinkDataFunction: ((data: ObjectData, model: Model) => ObjectData) | null;
  
  // Group Management (for nodes that act as groups)
  nodeGroupKeyProperty: string;
  getGroupKeyForNodeData(data: ObjectData): any;
  setGroupKeyForNodeData(data: ObjectData, key: any): void;
  
  // Member Queries
  findMemberNodeData(groupdata: ObjectData): ObjectData[];
  
  // Validation Functions
  isLinkValid(fromdata: ObjectData, todata: ObjectData, linkdata: ObjectData): boolean;
}

Usage Examples:

// Basic GraphLinksModel setup
const model = new go.GraphLinksModel([
  { key: 1, text: 'Node 1', color: 'lightblue' },
  { key: 2, text: 'Node 2', color: 'lightgreen' },
  { key: 3, text: 'Node 3', color: 'pink' }
], [
  { from: 1, to: 2, text: 'Link 1-2' },
  { from: 2, to: 3, text: 'Link 2-3' },
  { from: 1, to: 3, text: 'Link 1-3' }
]);

// Custom property configuration
const customModel = new go.GraphLinksModel({
  nodeKeyProperty: 'id',
  nodeCategoryProperty: 'type',
  linkFromKeyProperty: 'source',
  linkToKeyProperty: 'target',
  linkKeyProperty: 'linkId'
});

customModel.nodeDataArray = [
  { id: 'start', type: 'start', text: 'Start' },
  { id: 'process', type: 'process', text: 'Process Data' },
  { id: 'end', type: 'end', text: 'End' }
];

customModel.linkDataArray = [
  { linkId: 'l1', source: 'start', target: 'process' },
  { linkId: 'l2', source: 'process', target: 'end' }
];

// Model with groups
const groupModel = new go.GraphLinksModel({
  nodeGroupKeyProperty: 'group'
});

groupModel.nodeDataArray = [
  { key: 'group1', text: 'Group 1', isGroup: true },
  { key: 'a', text: 'Node A', group: 'group1' },
  { key: 'b', text: 'Node B', group: 'group1' },
  { key: 'c', text: 'Node C' }
];

groupModel.linkDataArray = [
  { from: 'a', to: 'b' },
  { from: 'b', to: 'c' }
];

TreeModel extends Model

Specialized model for hierarchical tree structures with parent-child relationships.

class TreeModel extends Model {
  /**
   * Creates a new TreeModel.
   * @param nodeDataArray - Array of node data objects
   * @param init - Optional initialization properties
   */
  constructor(nodeDataArray?: ObjectData[], init?: Partial<TreeModel>);
  
  // Tree Structure Properties
  nodeParentKeyProperty: string;
  nodeChildrenProperty: string;
  
  // Tree Operations
  getParentKeyForNodeData(data: ObjectData): any;
  setParentKeyForNodeData(data: ObjectData, key: any): void;
  getChildrenForNodeData(data: ObjectData): ObjectData[];
  
  // Tree Queries  
  findTreeRoots(): ObjectData[];
  findTreeParentNode(nodedata: ObjectData): ObjectData | null;
  findTreeChildrenNodes(nodedata: ObjectData): ObjectData[];
  findTreeLevel(nodedata: ObjectData): number;
  
  // Tree Modifications
  addNodeData(obj: ObjectData): void;
  removeNodeData(obj: ObjectData): void;
}

Usage Examples:

// Basic tree model
const treeModel = new go.TreeModel([
  { key: 1, text: 'Root' },
  { key: 2, text: 'Child 1', parent: 1 },
  { key: 3, text: 'Child 2', parent: 1 },
  { key: 4, text: 'Grandchild 1', parent: 2 },
  { key: 5, text: 'Grandchild 2', parent: 2 }
]);

// Tree model with custom properties
const orgChart = new go.TreeModel({
  nodeParentKeyProperty: 'boss',
  nodeKeyProperty: 'id'
});

orgChart.nodeDataArray = [
  { id: 'ceo', name: 'CEO', title: 'Chief Executive Officer' },
  { id: 'vp1', name: 'VP Sales', title: 'Vice President Sales', boss: 'ceo' },
  { id: 'vp2', name: 'VP Dev', title: 'Vice President Development', boss: 'ceo' },
  { id: 'mgr1', name: 'Sales Mgr', title: 'Sales Manager', boss: 'vp1' },
  { id: 'dev1', name: 'Developer 1', title: 'Senior Developer', boss: 'vp2' },
  { id: 'dev2', name: 'Developer 2', title: 'Junior Developer', boss: 'vp2' }
];

// Adding nodes to tree dynamically
orgChart.startTransaction('add employee');
orgChart.addNodeData({
  id: 'intern1',
  name: 'Intern',
  title: 'Development Intern',
  boss: 'dev1'
});
orgChart.commitTransaction('add employee');

Data Binding Integration

Binding Class

Connects model data properties to GraphObject visual properties with automatic updates.

class Binding {
  /**
   * Creates a new Binding.
   * @param targetProperty - GraphObject property to bind to
   * @param sourceProperty - Model data property to bind from (defaults to targetProperty)
   */
  constructor(targetProperty: string, sourceProperty?: string);
  
  name: string;           // Source property name
  property: string;       // Target property name
  converter?: (val: any, obj: GraphObject) => any;  // Value conversion function
  mode: BindingMode;      // OneWay or TwoWay
  
  // Fluent API
  transform(converter: (val: any, obj: GraphObject) => any): Binding;
  makeTwoWay(): Binding;
}

enum BindingMode {
  OneWay = 'OneWay',
  TwoWay = 'TwoWay'
}

Usage Examples:

// Basic data binding
const nodeTemplate = new go.Node('Auto')
.add(
  new go.Shape('RoundedRectangle')
    .bind('fill', 'color'),              // bind fill to data.color
  new go.TextBlock()
    .bind('text', 'name')                // bind text to data.name
    .bind('font', 'isImportant', (val) => // conditional formatting
      val ? 'bold 14pt sans-serif' : '12pt sans-serif'
    )
);

// Two-way binding for editable text
const editableTemplate = new go.Node('Auto')
.add(
  new go.Shape('Rectangle', { fill: 'white', stroke: 'black' }),
  new go.TextBlock({ 
    editable: true,
    margin: 4
  })
    .bindTwoWay('text', 'name')  // changes update the model
);

// Complex binding with conversion
const statusTemplate = new go.Node('Auto')
.add(
  new go.Shape('RoundedRectangle')
    .bind('fill', 'status', (status) => {
      switch (status) {
        case 'active': return 'lightgreen';
        case 'inactive': return 'lightgray';
        case 'error': return 'lightcoral';
        default: return 'white';
      }
    }),
  new go.TextBlock()
    .bind('text', '', (data) => `${data.name} (${data.status})`)
);

Model Events and Change Tracking

ChangedEvent

Represents changes to model data for undo/redo and data binding updates.

class ChangedEvent {
  model: Model;
  change: ChangeType;
  modelChange: string;
  isTransactionFinished: boolean;
  propertyName: string;
  object: any;
  oldValue: any;
  newValue: any;
  oldParam: any;
  newParam: any;
}

enum ChangeType {
  Transaction = 'Transaction',
  Property = 'Property',
  Insert = 'Insert',
  Remove = 'Remove'
}

Usage Examples:

// Listen to model changes
model.addChangedListener((e) => {
  if (e.change === go.ChangeType.Property) {
    console.log(`Property ${e.propertyName} changed from ${e.oldValue} to ${e.newValue}`);
  }
  
  if (e.change === go.ChangeType.Insert && e.modelChange === 'nodeDataArray') {
    console.log('Node added:', e.newValue);
  }
  
  if (e.isTransactionFinished) {
    console.log('Transaction completed');
    saveModelToServer(model.toJson());
  }
});

// Programmatically modify model data
model.startTransaction('update status');
model.setDataProperty(nodeData, 'status', 'completed');
model.setDataProperty(nodeData, 'completedDate', new Date());
model.commitTransaction('update status');

Common Patterns

Model Serialization

// Save model state
const jsonData = model.toJson();
localStorage.setItem('diagram-data', jsonData);

// Load model state
const savedData = localStorage.getItem('diagram-data');
if (savedData) {
  model.fromJson(savedData);
}

// Incremental updates for real-time collaboration
model.addChangedListener((e) => {
  if (e.isTransactionFinished) {
    const incrementalJson = model.toIncrementalJson(e);
    sendToServer(incrementalJson);
  }
});

Dynamic Model Updates

// Add nodes and links dynamically
function addEmployee(bossKey: any, employeeData: ObjectData) {
  model.startTransaction('add employee');
  
  // Add the new employee node
  employeeData.boss = bossKey;
  model.addNodeData(employeeData);
  
  model.commitTransaction('add employee');
}

// Remove node and all connected links (GraphLinksModel only)
function removeNodeAndLinks(nodeData: ObjectData) {
  model.startTransaction('remove node');
  
  // Remove all links connected to this node
  const links = model.findLinksForNode(nodeData);
  links.forEach(linkData => model.removeLinkData(linkData));
  
  // Remove the node
  model.removeNodeData(nodeData);
  
  model.commitTransaction('remove node');
}

Model Validation

// Custom validation functions
const validatedModel = new go.GraphLinksModel({
  nodeFilter: (data) => {
    // Only allow nodes with required properties
    return data.hasOwnProperty('id') && data.hasOwnProperty('text');
  },
  
  linkFilter: (data) => {
    // Only allow links with valid from/to keys
    return data.hasOwnProperty('from') && data.hasOwnProperty('to');
  }
});

// Override isLinkValid for custom link validation
validatedModel.isLinkValid = (fromdata, todata, linkdata) => {
  // Prevent self-links
  if (fromdata === todata) return false;
  
  // Prevent duplicate links
  const existingLinks = validatedModel.findLinksOutOf(fromdata);
  return !existingLinks.some(link => 
    validatedModel.getToKeyForLinkData(link) === validatedModel.getKeyForNodeData(todata)
  );
};

docs

binding-animation.md

core-diagram.md

data-models.md

geometry-collections.md

graphobject-hierarchy.md

index.md

interactive-tools.md

layout-system.md

theme-management.md

visual-elements.md

tile.json