Entity Relationship Diagrams in EJ2 JavaScript Diagram Control

2 Jul 202624 minutes to read

An Entity Relationship (ER) diagram is a visual representation of a database structure. It displays entities (such as tables), their attributes (such as columns), and the relationships between those entities. In the Syncfusion® Diagram control, ER diagrams can be created by configuring nodes with ErShapeModel and connectors with ErConnectorShapeModel.

ER entity nodes are added to the nodes property, and ER relationships are defined as connectors and added to the connectors property.

ER diagram elements

An ER diagram is built using the following main elements:

  • Entities - Represent database tables or objects, such as Customer, Order, or Product.
  • Fields - Represent columns or attributes inside an entity, such as CustomerID, Name, or Email.
  • Relationships - Represent how one entity is associated with another entity.

Creating ER entity nodes

An ER entity node represents a database entity, such as a table or object. It appears as a box that displays the entity name in the header and its fields as rows. The node shape can be defined by setting the type property to Er.

ej.diagrams.Diagram.Inject(ej.diagrams.ErDiagrams);

// Define a basic ER entity (Customer table)
var customer = {
  id: 'Customer',
  offsetX: 300,
  offsetY: 200,
  shape: {
    type: 'Er',
    header: {
      annotation: {
        content: 'Customer'
      }
    },
    fields: [
      {
        id: 'cust_id',
        name: 'CustomerID',
        dataType: 'INT',
        isPrimaryKey: true,
        constraints: ['NotNull']
      },
      {
        id: 'cust_firstname',
        name: 'FirstName',
        dataType: 'VARCHAR(50)',
        constraints: ['NotNull']
      },
      {
        id: 'cust_email',
        name: 'Email',
        dataType: 'VARCHAR(100)',
        constraints: ['Unique']
      }
    ]
  }
};

// Initialize diagram control
var diagram = new ej.diagrams.Diagram(
  {
    width: '100%',
    height: '400px',
    nodes: [customer]
  },
  '#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
    <title>EJ2 Diagram</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Typescript UI Controls">
    <meta name="author" content="Syncfusion">
    <link href="index.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-base/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-buttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-splitbuttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-diagrams/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-navigations/styles/tailwind3.css" rel="stylesheet">
    <script src="https://cdn.syncfusion.com/ej2/34.1.29/dist/ej2.min.js" type="text/javascript"></script>
</head>
<body style="margin-top: 100px;">
    <div id="container">
        <div id="element"></div>
    </div>
    <script>
        var ele = document.getElementById('container');
        if (ele) {
            ele.style.visibility = "visible";
        }   
    </script>
    <script src="index.js" type="text/javascript"></script>
</body>
</html>

Configure the entity header

The header is the top section of an ER entity node that displays the entity name. The header appearance can be customized using the header property.

ER Header Property Description
annotation Defines the text content displayed in the header.
height Defines the height of the header area in pixels.
style Defines style properties such as fill color, text color, and font settings.
ej.diagrams.Diagram.Inject(ej.diagrams.ErDiagrams);

// Define an ER entity with customized header
var customer = {
  id: 'Customer',
  offsetX: 300,
  offsetY: 200,
  shape: {
    type: 'Er',
    header: {
      annotation: {
        content: 'CUSTOMER TABLE',
        style: {
          color: 'white',
          fontSize: 13,
          bold: true,
          fontFamily: 'Arial'
        }
      },
      height: 35,
      style: {
        fill: '#2E75B6'
      }
    },
    fields: [
      {
        id: 'cust_id',
        name: 'CustomerID',
        dataType: 'INT',
        isPrimaryKey: true
      },
      {
        id: 'cust_name',
        name: 'FirstName',
        dataType: 'VARCHAR(50)'
      },
      {
        id: 'cust_email',
        name: 'Email',
        dataType: 'VARCHAR(100)'
      }
    ]
  }
};

// Initialize diagram control
var diagram = new ej.diagrams.Diagram(
  {
    width: '100%',
    height: '400px',
    nodes: [customer]
  },
  '#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
    <title>EJ2 Diagram</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Typescript UI Controls">
    <meta name="author" content="Syncfusion">
    <link href="index.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-base/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-buttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-splitbuttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-diagrams/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-navigations/styles/tailwind3.css" rel="stylesheet">
    <script src="https://cdn.syncfusion.com/ej2/34.1.29/dist/ej2.min.js" type="text/javascript"></script>
</head>
<body style="margin-top: 100px;">
    <div id="container">
        <div id="element"></div>
    </div>
    <script>
        var ele = document.getElementById('container');
        if (ele) {
            ele.style.visibility = "visible";
        }   
    </script>
    <script src="index.js" type="text/javascript"></script>
</body>
</html>

NOTE

If no header is specified, a default header is automatically added to the ER entity node with the default style and height.

Define entity fields

Fields represent the columns or attributes of an entity. They can be defined using the fields property. Each field can display information such as the field name, data type, and key constraints, including primary key, foreign key, unique, and not null.

ER Field Property Description
id Defines the unique identifier for the field within the entity.
name Defines the display name of the field.
dataType Defines the data type of the field, such as INT, VARCHAR(255), or BOOLEAN.
isPrimaryKey Indicates whether the field is the primary key of the entity.
isForeignKey Indicates whether the field is a foreign key that references another entity.
constraints Defines additional constraints applied to the field. Accepts one or more ErFieldConstraint values.
style Defines the visual style of the ER field row. Supports standard shape style properties such as fill, stroke color, stroke width, opacity, and other supported diagram style values. Field-level style values override applicable values from field defaults.
annotation Defines text styling for the ER field row. Only annotation style property is applicable. The annotation content property is ignored.
ej.diagrams.Diagram.Inject(ej.diagrams.ErDiagrams);

// Define an ER entity with various field properties
var product = {
  id: 'Product',
  offsetX: 300,
  offsetY: 200,
  shape: {
    type: 'Er',
    header: {
      annotation: {
        content: 'Product'
      }
    },
    fields: [
      {
        id: 'prod_id',
        name: 'ProductID',
        dataType: 'INT',
        isPrimaryKey: true,
        constraints: ['NotNull']
      },
      {
        id: 'prod_code',
        name: 'ProductCode',
        dataType: 'VARCHAR(50)',
        constraints: ['NotNull', 'Unique']
      },
      {
        id: 'prod_name',
        name: 'ProductName',
        dataType: 'VARCHAR(150)',
        constraints: ['NotNull']
      },
      {
        id: 'prod_price',
        name: 'Price',
        dataType: 'DECIMAL(10,2)',
        constraints: ['NotNull']
      },
      {
        id: 'prod_desc',
        name: 'Description',
        dataType: 'TEXT'
      },
      {
        id: 'prod_catid',
        name: 'CategoryID',
        dataType: 'INT',
        isForeignKey: true
      }
    ]
  }
};

// Initialize diagram control
var diagram = new ej.diagrams.Diagram(
  {
    width: '100%',
    height: '400px',
    nodes: [product]
  },
  '#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
    <title>EJ2 Diagram</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Typescript UI Controls">
    <meta name="author" content="Syncfusion">
    <link href="index.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-base/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-buttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-splitbuttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-diagrams/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-navigations/styles/tailwind3.css" rel="stylesheet">
    <script src="https://cdn.syncfusion.com/ej2/34.1.29/dist/ej2.min.js" type="text/javascript"></script>
</head>
<body style="margin-top: 100px;">
    <div id="container">
        <div id="element"></div>
    </div>
    <script>
        var ele = document.getElementById('container');
        if (ele) {
            ele.style.visibility = "visible";
        }   
    </script>
    <script src="index.js" type="text/javascript"></script>
</body>
</html>

NOTE

If no fields are specified, a default single field is automatically added to the ER entity node.

Add or remove ER fields at runtime

ER fields can be updated after the diagram is rendered by using the addErField and removeErField methods. These methods help add new fields to an ER entity node or remove existing fields without recreating the diagram.

The addErField method adds a field to an ER entity node.

var entityNode = diagram.nodes[0];
var newField = {
    id: 'customer_phone',
    name: 'Phone',
    dataType: 'VARCHAR(20)'
};

diagram.addErField(entityNode, newField);

To insert the field at a specific position, pass the index as the third argument:

diagram.addErField(entityNode, newField, 2);

The removeErField method removes an existing field from an ER entity node.

// Find the field that needs to be removed from the ER entity.
var fieldToRemove = entityNode.shape.fields.find(
    field => field.id === 'customer_phone'
);

if (fieldToRemove) {
    diagram.removeErField(entityNode, fieldToRemove);
}

Configure default field appearance

The fieldDefaults property defines the default visual appearance for all fields in an ER entity node. These settings are applied to every field unless they are overridden by individual field-level style settings.

ER Field Defaults Property Description
alternateRowColors Defines exactly two colors cycled across field rows in alternating order. Row 0 uses alternateRowColors[0], row 1 uses alternateRowColors[1], row 2 uses alternateRowColors[0], and so on.
height Defines the default height of each ER entity field row.

Style ER entities and fields

The appearance of ER entities and their fields can be customized using style properties. The node-level style property controls the overall ER entity appearance, while individual field style values can override applicable styles for specific field rows.

ej.diagrams.Diagram.Inject(ej.diagrams.ErDiagrams);

// Define ER entities with custom styling
var customer = {
  id: 'Customer',
  offsetX: 200,
  offsetY: 200,
  shape: {
    type: 'Er',
    header: {
      annotation: {
        content: 'CUSTOMER TABLE',
        style: {
          bold: true,
          color: 'white'
        }
      },
      height: 35,
      style: {
        fill: '#2E75B6',
        color: 'white',
        fontSize: 12,
        bold: true
      }
    },
    fields: [
      {
        id: 'cust_id',
        name: 'CustomerID',
        dataType: 'INT',
        isPrimaryKey: true
      },
      {
        id: 'cust_firstname',
        name: 'FirstName',
        dataType: 'VARCHAR(50)'
      },
      {
        id: 'cust_email',
        name: 'Email',
        dataType: 'VARCHAR(100)'
      }
    ],
    fieldDefaults: {
      alternateRowColors: ['#E7F0F7', '#ffffff']
    }
  },
  style: {
    fill: '#ffffff',
    strokeColor: '#2E75B6',
    strokeWidth: 1
  }
};

var product = {
  id: 'Product',
  offsetX: 600,
  offsetY: 200,
  shape: {
    type: 'Er',
    header: {
      annotation: {
        content: 'PRODUCT CATALOG',
        style: {
          bold: true,
          color: 'white'
        }
      },
      height: 35,
      style: {
        fill: '#70AD47',
        color: 'white',
        fontSize: 12,
        bold: true
      }
    },
    fields: [
      {
        id: 'prod_id',
        name: 'ProductID',
        dataType: 'INT',
        isPrimaryKey: true,
        style: {
          fill: '#FFE699'
        }
      },
      {
        id: 'prod_name',
        name: 'ProductName',
        dataType: 'VARCHAR(150)'
      },
      {
        id: 'prod_price',
        name: 'Price',
        dataType: 'DECIMAL(10,2)',
        style: {
          fill: '#C6E0B4'
        }
      }
    ],
    fieldDefaults: {
      alternateRowColors: ['#ffffff', '#F2F2F2']
    }
  },
  style: {
    fill: '#ffffff',
    strokeColor: '#70AD47',
    strokeWidth: 1.5
  }
};

// Initialize diagram control
var diagram = new ej.diagrams.Diagram(
  {
    width: '100%',
    height: '400px',
    nodes: [customer, product]
  },
  '#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
    <title>EJ2 Diagram</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Typescript UI Controls">
    <meta name="author" content="Syncfusion">
    <link href="index.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-base/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-buttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-splitbuttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-diagrams/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-navigations/styles/tailwind3.css" rel="stylesheet">
    <script src="https://cdn.syncfusion.com/ej2/34.1.29/dist/ej2.min.js" type="text/javascript"></script>
</head>
<body style="margin-top: 100px;">
    <div id="container">
        <div id="element"></div>
    </div>
    <script>
        var ele = document.getElementById('container');
        if (ele) {
            ele.style.visibility = "visible";
        }   
    </script>
    <script src="index.js" type="text/javascript"></script>
</body>
</html>

NOTE

Field-level styles override applicable node-level and field default styles.

Track entity field changes

The erEntityChanged event is triggered when ER entity fields are added, removed, or reordered. This event provides the previous and updated entity states, which can be used to track modifications, validate field changes, or synchronize updates with an external data source.

erEntityChanged: (args) => {
    if (args.cause === 'FieldsReorder' && args.state === 'Completed') {
        console.log('ER fields reordered successfully.');
    }
}

Creating ER relationships

Relationships define how one ER entity is connected to another entity. In the Diagram control, relationships are created using ER connectors. They are rendered as lines with multiplicity symbols at the connector ends.The connector shape can be defined by setting the type property to Er.

ER Connector Shape Property Description
type Defines the connector shape type as 'Er'. Default: 'Er'.
relationship Defines whether the relationship is identifying or non-identifying.
sourceMultiplicity Defines the Crow’s Foot multiplicity rendered at the source end of the ER connector.
targetMultiplicity Defines the Crow’s Foot multiplicity rendered at the target end of the ER connector.

Define relationship multiplicity

Multiplicity defines how many instances of one entity can be associated with instances of another entity. In ER diagrams, multiplicity is represented using Crow’s Foot symbols at the source and target ends of a connector.

Multiplicity Type Meaning Example Image
One Represents a single participation marker. A customer has one primary account. One Multiplicity
OneAndOnlyOne Represents mandatory participation of exactly one instance. A user must have exactly one profile. OneAndOnlyOne Multiplicity
Many Represents multiple instances. A customer can have many orders. Many Multiplicity
ZeroOrOne Represents zero or one instance. An employee may have zero or one manager badge. ZeroOrOne Multiplicity
OneOrMany Represents one or more instances. A department must have one or more employees. OneOrMany Multiplicity
ZeroOrMany Represents zero or more instances. A customer may have zero or more wish list items. ZeroOrMany Multiplicity
ej.diagrams.Diagram.Inject(ej.diagrams.ErDiagrams);

var customer = {
  id: 'Customer',
  offsetX: 250,
  offsetY: 100,
  shape: {
    type: 'Er',
    header: {
      annotation: {
        content: 'Customer'
      }
    },
    fields: [
      {
        id: 'customer_id',
        name: 'CustomerID',
        dataType: 'INT',
        isPrimaryKey: true
      }
    ]
  },
  style: {
    fill: '#ffffff',
    strokeColor: '#7c3aed',
    strokeWidth: 1.5
  }
};

var order = {
  id: 'Order',
  offsetX: 400,
  offsetY: 250,
  shape: {
    type: 'Er',
    header: {
      annotation: {
        content: 'Order'
      }
    },
    fields: [
      {
        id: 'order_id',
        name: 'OrderID',
        dataType: 'INT',
        isPrimaryKey: true
      },
      {
        id: 'customer_id_fk',
        name: 'CustomerID',
        dataType: 'INT',
        isForeignKey: true
      }
    ]
  },
  style: {
    fill: '#ffffff',
    strokeColor: '#7c3aed',
    strokeWidth: 1.5
  }
};

var relationship = {
  id: 'customer_order',
  sourceID: 'Customer',
  targetID: 'Order',
  shape: {
    type: 'Er',
    sourceMultiplicity: {
      type: 'One'
    },
    targetMultiplicity: {
      type: 'OneOrMany'
    }
  },
  style: {
    strokeColor: '#7c3aed',
    strokeWidth: 1.5
  },
  sourceDecorator: {
    style: {
      strokeColor: '#7c3aed',
      strokeWidth: 1.5
    }
  },
  targetDecorator: {
    style: {
      strokeColor: '#7c3aed',
      strokeWidth: 1.5
    }
  }
};

var diagram = new ej.diagrams.Diagram(
  {
    width: '100%',
    height: '400px',
    nodes: [customer, order],
    connectors: [relationship],
    getConnectorDefaults: (connector) => {
      connector.cornerRadius = 10;
      connector.type = 'Orthogonal';
      return connector;
    }
  },
  '#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
    <title>EJ2 Diagram</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Typescript UI Controls">
    <meta name="author" content="Syncfusion">
    <link href="index.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-base/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-buttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-popups/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-splitbuttons/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-diagrams/styles/tailwind3.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/34.1.29/ej2-navigations/styles/tailwind3.css" rel="stylesheet">
    <script src="https://cdn.syncfusion.com/ej2/34.1.29/dist/ej2.min.js" type="text/javascript"></script>
</head>
<body style="margin-top: 100px;">
    <div id="container">
        <div id="element"></div>
    </div>
    <script>
        var ele = document.getElementById('container');
        if (ele) {
            ele.style.visibility = "visible";
        }   
    </script>
    <script src="index.js" type="text/javascript"></script>
</body>
</html>

See also