TL;DR: This article explores how to leverage Syncfusion’s Angular Diagram Library to design interactive UML class diagrams. It walks you through the process, from setting up your environment to rendering UML shapes and relationships within your diagram.
UML (Unified Modeling Language) Class Diagrams serve as a visual representation of an app’s static structure, playing a pivotal role in the modeling of object-oriented systems.
A class diagram shows the classes, attributes, operations, and the relationship between them. This helps software engineers in developing the code for an app. It is also used for describing, visualizing, and documenting different facets of a system. Here’s a brief overview of the key components:
In this article, we’ll explore how to create interactive UML class diagrams using the Syncfusion Angular Diagram Library.
Before you begin, ensure you have installed the Node.js version 16.14.0.
Let’s create a diagram surface by following these steps.
Step 1: Create a folder and name it as a UML Class Diagram.
Step 2: Open your terminal or command prompt and install the Angular CLI globally using the following command.
npm install -g @angular/cli
Step 3: Create a new Angular app, use the following command, and replace my-app with your desired app name. Here, we’ll name the app as logic-circuit.
ng new uml-class
Step 4: Navigate to your app’s directory. Change your working directory to the newly created app using the following command.
cd uml-class
Step 5: Run the following command to launch your Angular app.
ng serve
This will open your app in your default web browser at http://localhost:4200/.
Step 6: Then, open the package.json file and add the following necessary dependency packages.
"dependencies": { "rxjs": "7.8.1", "tslib": "2.6.3", "zone.js": "0.14.2", "@angular/core": "17.0.1", "@angular/forms": "17.0.1", "@angular/common": "17.0.1", "@angular/router": "17.0.1", "@angular/compiler": "17.0.1", "@angular/animations": "17.0.1", "@angular/platform-browser": "17.0.1", "@syncfusion/ej2-base": "26.1.37", "@syncfusion/ej2-angular-base": "26.1.41", "@syncfusion/ej2-diagrams": "26.1.41", "@syncfusion/ej2-angular-diagrams": "26.1.41", "@syncfusion/ej2-buttons": "26.1.40", "@syncfusion/ej2-data": "26.1.41", "@syncfusion/ej2-inputs": "26.1.41", "@syncfusion/ej2-lists": "26.1.35", "@syncfusion/ej2-navigations": "26.1.41", "@syncfusion/ej2-popups": "26.1.41", "codemirror": "5.65.15", "marked": "0.3.19", "@types/codemirror": "0.0.56", "@types/marked": "0.3.0", "@angular/http": "7.2.16", "rxjs-compat": "6.6.7", "reflect-metadata": "0.2.2", "core-js": "2.6.9" },
Step 7: Use the following command to install all dependent packages.
npm install
Step 8: Now, add the dependent scripts and style CDN reference links to the index.html file.
<head> <meta charset="utf-8"> <title>Angular UML Class Diagram</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script> <link href="https://cdn.syncfusion.com/ej2/26.1.35/material.css" rel="stylesheet"/> </head>
Step 9: To include the Diagram component in your app, import it from the ej2-angular-diagrams package. The required arguments, such as the width, height, and collection of nodes and connectors, must be included in the app.component.html file.
Refer to the following code example.
<div id="diagram-space" class="sb-mobile-diagram"> <ejs-diagram #diagram id="diagram" width="100%" height="100%" > </ejs-diagram> </div >
The following image shows the created diagramming page.
Note: For more details, refer to the Getting started with Angular Diagram Library documentation.
Syncfusion Angular Diagram Library supports three different types of UML class shapes.
A class defines a group of objects that share common specifications, features, constraints, and semantics. To create a class object, the classifier should be defined using the class notation. This notation serves as a foundational element in object-oriented programming, encapsulating the essential characteristics and behavior that the objects belonging to the class will exhibit. Also, define the name, attributes, and methods of the class using the classShape property of the node.
The attribute’s name, type, and scope properties allow you to define the name, data type, and visibility of the attribute, respectively. The scope property enables you to specify the visibility of class members in UML shapes. It helps indicate the level of access control applied to the attributes and methods of a class.
The method’s name, parameters, type, and scope properties allow you to define the name, parameter, return type, and visibility of the methods. The method parameters object properties allow you to define the name and type of the parameter.
Refer to the following code example.
public nodes: NodeModel[] = [ { id: "node1", //Position of the node offsetX: 200, offsetY: 200, style: { fill: '#26A0DA', }, shape: { type: "UmlClassifier", //Define class object classShape: { name: "Patient", //Define class attributes attributes: [{ name: "accepted", type: "Date" }], //Define class methods methods: [{ name: "getHistory", type: "getHistory" }] }, classifier: "Class" } as UmlClassifierShapeModel } ];
An interface is a specific type of classifier that signifies a declaration of a cohesive set of public features and obligations.
Creating an interface involves defining the classifier property using the interface notation. This essential concept in object-oriented programming outlines a contract for classes to adhere to, specifying the required methods and behaviors without delving into the implementation details.
We can also define the name, attributes, and methods of the interface using the interface property of the node. The attribute’s name, type, and scope properties allow you to define the name, data type, and visibility of the attribute. The method’s name, parameter, type, and scope properties allow you to define the name, parameter, return type, and visibility of the methods, respectively.
Refer to the following code example.
public nodes: NodeModel[] = [ { id: 'node1', // Position of the node offsetX: 200, offsetY: 200, style: { fill: '#26A0DA', }, shape: { type: 'UmlClassifier', // Define interface object interfaceShape: { name: 'Patient', // Define interface attributes attributes: [{ name: 'owner', type: 'String[*]' }], // Define interface methods methods: [ { name: 'deposit', parameters: [ { name: 'amount', type: 'Dollars', }, ], }, ], }, classifier: 'Interface', } as UmlClassifierShapeModel, }, ];
To establish an enumeration, designate the classifier property of the node as enumeration. Additionally, define the name and enumerate the members of the enumeration using the appropriate enumeration property of the node. This process encapsulates a set of distinct values within the enumeration, allowing for a clear representation of specific, named constants within a system. You can set a name for the enumeration members collection using the name property of the members collection.
Refer to the following code example.
public nodes: NodeModel[] = [ { id: "node1", offsetX: 300, offsetY: 300, style: { fill: '#26A0DA', }, shape: { type: 'UmlClassifier', enumerationShape: { name: 'AccountType', members: [ { name: 'Current Account', style: {} }, { name: 'Savings Account' }, { name: 'Credit Account' } ] }, classifier: 'Enumeration' }, } ];
In UML class diagrams, relationships between classes illustrate how objects of different classes interact with each other. These relationships are crucial for understanding the structure and behavior of a system. Syncfusion Angular Diagram library supports the following types of UML class relationships:
An association represents a structural relationship between classes, where objects of one class are connected to objects of another class. It illustrates how instances of these two classes are linked, defining the direction of the UML connectors.
Refer to the following code example.
public connectors: ConnectorModel[] = [ { id: 'connector1', // Define connector start and end points sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 300 }, type: 'Straight', shape: { type: 'UmlClassifier', relationship: 'Association', // Define type of association associationType: 'Directional', }, }, { id: 'connector2', // Define connector start and end points sourcePoint: { x: 200, y: 100 }, targetPoint: { x: 400, y: 300 }, type: 'Straight', shape: { type: 'UmlClassifier', relationship: 'Association', associationType: 'BiDirectional', }, }, ];
Aggregation is a binary association between a property and one or more composite objects that group a set of instances. It is decorated with a hollow diamond. To create an aggregation shape, define the relationship as Aggregation.
The following code illustrates how to create an aggregation.
public connectors: ConnectorModel[] = [ { id: "connector", sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 300 }, type: "Straight", shape: { type: "UmlClassifier", relationship: "Aggregation" } } ];
Composition is a strong form of aggregation. It is decorated with a black diamond. To create a composition shape, define the relationship property of the connector as Composition.
The following code example demonstrates how to implement a composition.
public connectors: ConnectorModel[] = [ { id: "connector", sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 300 }, type: "Straight", shape: { type: "UmlClassifier", relationship: "Composition" } } ];
Dependency is a directed relationship that shows whether some UML elements need or depend on other model elements for specifications. It is shown as a dashed line with an opened arrow. To create a dependency, define the relationship property of the connector as a Dependency.
Refer to the following code example.
public connectors: ConnectorModel[] = [ { id: "connector", sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 300 }, type: "Straight", shape: { type: "UmlClassifier", relationship: "Dependency" } } ];
Inheritance is also called a generalization. It is a binary taxonomic directed relationship between a more general classifier (superclass) and a more specific classifier (subclass). Inheritance is shown as a line with a hollow triangle. To create an inheritance, define the relationship as Inheritance.
The following code demonstrates how to create an inheritance relationship.
public connectors: ConnectorModel[] = [ { id: "connector", sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 300 }, type: "Straight", shape: { type: "UmlClassifier", relationship: "Inheritance" } } ];
Multiplicity is a definition of an inclusive interval of non-negative integers to specify the allowable number of instances of a described element. The types of multiplicity are as follows:
By default, the multiplicity will be considered OneToOne. The multiplicity property in UML allows you to specify a large number of elements or some collection of elements. The shape multiplicity’s source property is used to set the source label to the connector, and the target property is used to set the target label to the connector.
To set an optionality or cardinality for the connector source label, use the optional property. The lowerBounds and upperBounds could be natural constants or constant expressions evaluated to a natural (non-negative) number. The upper bound could also be specified as an asterisk *, which denotes unlimited elements. The upper bound should be greater than or equal to the lower bound.
The following code example demonstrates how to customize multiplicity settings effectively.
public connectors: ConnectorModel[] = [ { id: 'connector1', // Define connector start and end points sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 300 }, type: 'Straight', shape: { type: 'UmlClassifier', relationship: 'Dependency', multiplicity: { // Set multiplicity type type: 'OneToOne', }, }, }, { id: 'connector2', // Define connector start and end points sourcePoint: { x: 200, y: 100 }, targetPoint: { x: 400, y: 300 }, type: 'Straight', shape: { type: 'UmlClassifier', relationship: 'Dependency', multiplicity: { // Set multiplicity type type: 'ManyToOne', // Set source label to connector source: { optional: true, lowerBounds: '89', upperBounds: '67', }, // Set target label to connector target: { optional: true, lowerBounds: '78', upperBounds: '90', }, }, }, }, { id: 'connector3', // Define connector start and end points sourcePoint: { x: 300, y: 100 }, targetPoint: { x: 500, y: 300 }, type: 'Straight', shape: { type: 'UmlClassifier', relationship: 'Dependency', multiplicity: { // Set multiplicity type type: 'OneToMany', // Set source label to connector source: { optional: true, lowerBounds: '89', upperBounds: '67', }, // Set target label to connector target: { optional: true, lowerBounds: '78', upperBounds: '90', }, }, }, }, { id: 'connector4', // Define connector start and end points sourcePoint: { x: 400, y: 100 }, targetPoint: { x: 600, y: 300 }, type: 'Straight', shape: { type: 'UmlClassifier', relationship: 'Dependency', multiplicity: { // Set multiplicity type type: 'ManyToMany', // Set source label to connector source: { optional: true, lowerBounds: '89', upperBounds: '67', }, // Set target label to connector target: { optional: true, lowerBounds: '78', upperBounds: '90', }, }, }, }, ];
The Angular Diagram control provides a gallery of reusable nodes and connectors called SymbolPalettes. It displays a collection of palettes, each showing a set of nodes and connectors. We can drag and drop them onto the diagram canvas any number of times.
The UML built-in shapes can be easily rendered in a symbol palette. The symbols property of palettes is used to define UML symbols using the necessary classes and methods. This feature allows you to add a collection of predefined UML symbols to the palette, making your UML diagramming app more versatile.
Follow these steps to create a diagram symbol palette with UML class shapes.
Start by creating an HTML <div> element to act as the container for the symbol palette.
<div class="db-palette-parent"> <ejs-symbolpalette id="symbolpalette" width="100%" height="100%" ></ejs-symbolpalette> </div>
Next, initialize the Syncfusion Angular Diagram symbol palette by specifying the necessary parameters, such as width and height, and the collection of symbols to be included in the palette.
Refer to the following code example.
<div class="db-palette-parent"> <ejs-symbolpalette id="symbolpalette" [expandMode]='palettes.expandMode' [palettes]='palettes.palettes' width="100%" height="100%" [symbolMargin]='palettes.symbolMargin' [getSymbolInfo]='palettes.getSymbolInfo' > </ejs-symbolpalette> </div>
Define the collection of symbols to be included in the palette, which can be customized according to your needs. Here, we have added the UML class shapes and relationships.
public palettes: PaletteModel[] = [ { id: 'UmlActivity', expanded: true, title: 'UML Classifier Nodes', symbols: [ { id: 'class', style: { fill: '#26A0DA', }, borderColor: 'white', shape: { type: 'UmlClassifier', classShape: { attributes: [ { name: 'accepted', type: 'Date', style: { color: "red", fontFamily: "Arial", textDecoration: 'Underline', italic: true }, isSeparator: true }, { name: 'sickness', type: 'History' }, { name: 'prescription', type: 'String[*]' }, { name: 'allergies', type: 'String[*]' } ], methods: [ { name: 'getHistory', style: {}, parameters: [{ name: 'Date', style: {} }], type: 'History' } ], name: 'Patient' }, classifier: 'Class' }, }, { id: 'Interface', style: { fill: '#26A0DA', }, borderColor: 'white', shape: { type: 'UmlClassifier', interfaceShape: { name: "Bank Account", attributes: [ { name: "owner", type: "String[*]", style: {} }, { name: "balance", type: "Dollars" } ], methods: [ { name: "deposit", style: {}, parameters: [ { name: "amount", type: "Dollars", style: {} } ], } ] }, classifier: 'Interface' }, }, { id: 'Enumeration', style: { fill: '#26A0DA', }, borderColor: 'white', shape: { type: 'UmlClassifier', enumerationShape: { name: 'AccountType', members: [ { name: 'Checking Account', style: {} }, { name: 'Savings Account' }, { name: 'Credit Account' } ] }, classifier: 'Enumeration' }, }, ] }, { id: 'umlConnectorrs', expanded: true, title: 'UML Classifier Connectors', symbols: [ { id: 'Composition', sourcePoint: { x: 100, y: 200 }, targetPoint: { x: 200, y: 300 }, type: 'Straight', shape: { type: 'UmlClassifier', relationship: 'Composition' } }, { id: 'BiDirectional', type: 'Straight', sourcePoint: { x: 300, y: 200 }, targetPoint: { x: 400, y: 300 }, shape: { type: 'UmlClassifier', relationship: 'Aggregation', associationType: 'BiDirectional' } }, { id: 'Directional', type: 'Straight', sourcePoint: { x: 500, y: 200 }, targetPoint: { x: 600, y: 300 }, shape: { type: 'UmlClassifier', relationship: 'Association', associationType: 'Directional' } }, { id: 'Association', type: 'Straight', sourcePoint: { x: 700, y: 200 }, targetPoint: { x: 800, y: 300 }, shape: { type: 'UmlClassifier', relationship: 'Association' } }, { id: 'Inheritance', type: 'Straight', sourcePoint: { x: 900, y: 200 }, targetPoint: { x: 1000, y: 300 }, shape: { type: 'UmlClassifier', relationship: 'Inheritance' } }, { id: 'Interfaces', type: 'Straight', sourcePoint: { x: 100, y: 400 }, targetPoint: { x: 200, y: 500 }, shape: { type: 'UmlClassifier', relationship: 'Interface' } }, { id: 'Dependency', type: 'Straight', sourcePoint: { x: 300, y: 400 }, targetPoint: { x: 400, y: 500 }, shape: { type: 'UmlClassifier', relationship: 'Dependency' } }, { id: 'Realization', type: 'Straight', sourcePoint: { x: 500, y: 400 }, targetPoint: { x: 600, y: 500 }, shape: { type: 'UmlClassifier', relationship: 'Realization' } }, { id: "OneToMany", type: 'Straight', sourcePoint: { x: 700, y: 400 }, targetPoint: { x: 800, y: 500 }, annotations: [{ margin: { top: 10, left: 10, right: 10, bottom: 20 } }], shape: { type: "UmlClassifier", relationship: 'Dependency', multiplicity: { type: 'OneToMany', source: { optional: true, lowerBounds: '89', upperBounds: '67' }, target: { optional: true, lowerBounds: '78', upperBounds: '90' } } } }, { id: "ManyToMany", sourcePoint: { x: 900, y: 400 }, targetPoint: { x: 1000, y: 500 }, annotations: [{ margin: { top: 10, left: 10, right: 10, bottom: 20 } }], shape: { type: "UmlClassifier", relationship: 'Dependency', multiplicity: { type: 'ManyToMany', source: { optional: true, lowerBounds: '89', upperBounds: '67' }, target: { optional: true, lowerBounds: '78', upperBounds: '90' } } } }, { id: "OneToOne", sourcePoint: { x: 100, y: 600 }, targetPoint: { x: 200, y: 700 }, annotations: [{ margin: { top: 10, left: 10, right: 10, bottom: 20 } }], shape: { type: "UmlClassifier", relationship: 'Dependency', multiplicity: { type: 'OneToOne', source: { optional: true, lowerBounds: '89', upperBounds: '67' }, target: { optional: true, lowerBounds: '78', upperBounds: '90' } } } }, { id: "ManyToOne", sourcePoint: { x: 300, y: 600 }, targetPoint: { x: 400, y: 700 }, annotations: [{ margin: { top: 10, left: 10, right: 10, bottom: 20 } }], shape: { type: "UmlClassifier", relationship: 'Dependency', multiplicity: { type: 'ManyToOne', source: { optional: true, lowerBounds: '89', upperBounds: '67' }, target: { optional: true, lowerBounds: '78', upperBounds: '90' } } } }, { id: "OneToMany", sourcePoint: { x: 500, y: 600 }, targetPoint: { x: 600, y: 700 }, annotations: [{ margin: { top: 10, left: 10, right: 10, bottom: 20 } }], shape: { type: "UmlClassifier", relationship: 'Dependency', multiplicity: { type: 'OneToMany', } } } ] } ];
Refer to the following image.
Note: Refer to the symbol palette in Angular Diagram documentation for more details on adding symbols, grouping, and customizing the symbol palette appearance.
In UML nodes, child elements such as members, methods, and attributes can be added either programmatically or interactively.
The addChildToUmlNode method dynamically adds a child to the UML node during runtime, providing flexibility in modifying the diagram structure programmatically.
The following code example illustrates how to add members, methods, and attributes to the UML node at runtime.
case 'Add attribute': let node = this.diagram.nameTable['Patient']; let attribute = { name: 'accepted', type: 'Date', style: { color: 'red' } }; this.diagram.addChildToUmlNode(node, attribute, 'Attribute'); break; case 'Add Method': let node1 = this.diagram.nameTable['Person']; let method = { name: 'getHistory', style: { color: 'red' }, parameters: [{ name: 'Date', style: {} }], type: 'History', }; this.diagram.addChildToUmlNode(node1, method, 'Method'); break;
To include a child, select a node, move the mouse outside it, and position the pointer near the right side edge of the shape. A highlighter emerges between the two child elements. Click the highlighter to add a child type to the chosen UML node seamlessly.
The following gif illustrates how to add a child through user interaction.
The Angular Diagram Library provides a useful feature that allows you to save your work and resume it later by loading the saved diagram back onto the diagram canvas.
To save your current diagram, select the Save Diagram option in the toolbar. This will save your diagram as a file on your local drive.
To load an existing diagram file, select the Open Diagram option in the toolbar. This will open the file dialog box. From there, you can browse and select the saved diagram file that you want to load.
This feature provides great flexibility and convenience, allowing you to easily pick up where you left off on a diagram or to make changes to a previously saved diagram. It’s an essential feature for any diagramming app that enables users to create and manage complex diagrams.
Refer to the following GIF image.
You can export the created UML class diagram as an image file in different formats, such as JPEG, PNG, and SVG. By exporting the diagram as an image file, you can easily share it via email or other digital means or embed it in a document or presentation.
To do this, click the export button in the toolbar and select the required image file format to save the UML class diagram. You can also export only the content area of the diagram by excluding the blank areas or export the entire diagram (including blank areas) based on the width and height specified in the page settings.
To print a diagram, click the Print button in the toolbar. This will open the Print dialog box, where you can select your printer and customize the print settings, such as orientation, paper size, and page margins. Then, click on the Print button to print the floor planner diagram.
The Angular Diagram Library supports the following panning and zooming options.
For more details, refer to the Creating a UML class diagram using the Angular Diagram Library demo on StackBlitz.
Thanks for reading! This blog shows how to easily create a UML class diagram using the Syncfusion Angular Diagram Library. Similarly, you can create diagram creation apps like an organization chart creator, a flow chart creator, or a network diagram creator.
If you’re already a Syncfusion user, you can download the product setup from our website. Otherwise, you can download a free 30-day trial.
Please let us know in the comments section below if you have any questions. You can also contact us through our support forum, support portal, or feedback portal. We are always happy to assist you!