History List in Diagram

3 Jan 202310 minutes to read

Diagram tracks the history of actions that are performed after initializing the diagram and provides support to reverse and restore those changes.

Undo and redo

Diagram provides built-in support to track the changes that are made through interaction and through public APIs. The changes can be reverted or restored either through shortcut keys or through commands.

Undo/redo through shortcut keys

Undo/redo commands can be executed through shortcut keys. Shortcut key for undo is Ctrl+z and shortcut key for redo is Ctrl+y.

Undo/redo through public APIs

The client-side methods undo and redo helps to revert/restore the changes.

var diagram = document.getElementById("diagram").ej2_instances[0];

// Reverts the last action performed
diagram.undo();

// Restores the last undone action
diagram.redo();

When a change in the diagram is reverted or restored (undo/redo), the historyChange event gets triggered.

Group multiple changes

History list allows to revert or restore multiple changes through a single undo/redo command. For example, revert/restore the fill color change of multiple elements at a time.

The client-side method startGroupAction is used to notify the diagram to start grouping the changes. The client-side method endGroupAction is used to notify to stop grouping the changes.

<div class="col-lg-12 control-section">
    <div class="content-wrapper">
        @(Html.EJS().Diagram("container")
        .Width("100%")
        .Height("500px")
        .Nodes(ViewBag.nodes).Render())
    </div>
</div>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Syncfusion.EJ2.Diagrams;
using System.Drawing;

namespace EJ2MVCSampleBrowser.Controllers.Diagram {
    public partial class DiagramController: Controller {
        // GET: Nodes
        public ActionResult Nodes() {
            List < DiagramNode > nodes = new List < DiagramNode > ();
            List < DiagramNodeAnnotation > Node1 = new List < DiagramNodeAnnotation > ();
            Node1.Add(new DiagramNodeAnnotation() {
                Content = "node1", Style = new DiagramTextStyle() {
                    Color = "White", StrokeColor = "None"
                }
            });
            List < DiagramNodeAnnotation > Node2 = new List < DiagramNodeAnnotation > ();
            Node2.Add(new DiagramNodeAnnotation() {
                Content = "Node2"
            });
            List < DiagramNodeAnnotation > Node3 = new List < DiagramNodeAnnotation > ();
            Node3.Add(new DiagramNodeAnnotation() {
                Content = "Node3"
            });
            nodes.Add(new Node() {
                Id = "node1",
                    Width = 100,
                    Height = 100,
                    Style = new NodeStyleNodes() {
                        fill = "#6BA5D7",
                            strokeColor = "White"
                    },
                    text = "node1",
                    OffsetX = 100,
                    OffsetY = 100,
                    Annotations = Node1,
            });
            nodes.Add(new Node() {
                Id = "node2", OffsetX = 100, OffsetY = 170, Annotations = Node2
            });
            nodes.Add(new Node() {
                Id = "node3", OffsetX = 100, OffsetY = 240, Annotations = Node3
            });
            ViewBag.nodes = nodes;

            return View();
        }
    }
    public class Node: DiagramNode {
        public string text;
    }
}
var diagram = document.getElementById('container').ej2_instances[0];
    diagram.startGroupAction();
    //Makes the changes
    var color = ['black', 'red', 'green', 'yellow']
    for (var i = 0; i < color.length; i++) {
        // Updates the fillColor for all the child elements.
        diagram.nodes[0].style.fill = color[i];
        diagram.dataBind();
    }
    //Ends grouping the changes
    diagram.endGroupAction();

Track custom changes

Diagram provides options to track the changes that are made to custom properties. For example, in case of an employee relationship diagram, track the changes in the employee information. The historyList of the diagram enables to track such changes.

Before changing the employee information, save the existing information to historyList by using the client-side method push of historyList. The historyList canLog method can be used which takes a history entry as argument and returns whether the specific entry can be added or not.

var diagram = document.getElementById('container').ej2_instances[0];
//Creates a custom entry
var entry = {
    undoObject: diagram.nodes[0];
};
// adds that to history list
diagram.historyList.push(entry);
diagram.dataBind();

canLog

canLog in the history list, which takes a history entry as argument and returns whether the specific entry can be added or not.

var diagram = document.getElementById('container').ej2_instances[0];
diagram.historyList.canLog = function(entry) {
    entry.cancel = true;
    return entry;
}

Track undo/redo actions

The historyList undoStack property is used to get the collection of undo actions which should be performed in the diagram. The undoStack/redoStack is the read-only property.

var diagram = document.getElementById('container').ej2_instances[0];
//get the collection of undoStack objects
let undoStack = diagram.historyList.undoStack;
//get the collection of redoStack objects
let redoStack = diagram.historyList.redoStack;

History change event

The historyChange event triggers, whenever the interaction of the node and connector is take place.

var diagram = document.getElementById('container').ej2_instances[0];
// history change event
diagram.historyChange = (arg) => {
    //causes of history change
    let cause: string = arg.cause;
}

Retain Selection

You can retain a selection at undo/redo operation by using the client-side API Method called updateSelection. Using this method, you can select diagram objects.

let diagramInstance: DiagramComponent;
ReactDOM.render( < DiagramComponent id = "diagram" ref={diagram => diagramInstance = diagram}
        width = {
            '100%'
        }
        height = {
            '600px'
        }
        nodes = {
            nodes
        }
        />,   document.getElementById("diagram") );
        // history change event
        diagramInstance.updateSelection: (object: NodeModel, diagram: Diagram) => {
                    let selArr = [];
                    selArr.push(object)
                    diagram.select(selArr);
                },