Custom rendering in Network Graph
This is the second article about the network graph SAP UI5 control. The first article, you can find it here.
In this article, we will discuss custom rendering. As of SAP UI5 version 1.58, we allow UI5 developers to completely customize node visualization by changing the positions of icons and nodes titles , as well as applying custom colors and different node shapes.
We will take a look at three types of custom nodes:
- Custom box node — We will change the default locations of text and icons.
- Custom circular node — We will put the text into the circle where the node icon is placed by default.
- Custom-shaped node.
The Architecture of Custom Rendering
To enable custom rendering, you need to override some of the node’s methods. Although you can override methods for each node’s instance, I recommend that you create your own custom node class and inherit it from the default node.
Here, as you can see, we are creating our own class with the method calculateSizes. We will discuss this method later.
<custom:NetworkGraphCustomNode key="{key}"
title="{title}"
icon="{icon}"
shape="{shape}"
width="{width}"
height="{height}"
titleLineSize="{titleLineSize}"
status="{status}">
</custom:NetworkGraphCustomNode>
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"sap/suite/ui/commons/networkgraph/Node"
], function (Controller, JSONModel, Node) {
var oCustomNode = Node.extend("sap.suite.ui.commons.sample.NetworkGraphCustomRendering.NetworkGraphCustomNode", {
oCustomTitleSize: null,
oCustomTrimmedTextSize: null,
sBigFont: "BIG FONT TRIMMED TEXT",
calculateSizes: function () {
Text Wrapping
Before we start customization, there is a very important topic we need to discuss. For now, SVG doesn’t support wrapping text into multiple lines (the CSS equivalent is white-space: normal). That’s why we have provided two methods:
- calculateSizes — a method that calculates the number of lines of text.
- renderText — a method for rendering such calculated lines of text.
In the network graph control, this is unfortunately more complicated, as we need to know the node’s dimensions before the nodes are sent to the layout rendering processor.
That being said, without getting too deep into technical details, the node has a method calculateSizes which by default calculates and positions all texts when the default rendering algorithm is used.
If you want to use custom rendering, you can override this function (best in your own class inheriting from the node) and do the calculation for your text yourself. There is another method that helps you calculate text size,computeTextDimensions – you only need to specify the width of the text, the maximum number of lines (0 for unlimited), and, of course, the text itself.
calculateSizes: function () {
this.oCustomTitleSize = this.computeTextDimmensions({
text: this.getTitle(),
width: this.getWidth() - 10,
maxLines: this.getTitleLineSize()
});
this.oCustomTrimmedTextSize = this.computeTextDimmensions({
text: this.sBigFont,
width: this.getWidth() - 10,
attributes: {
"font-size": "20px"
},
maxLines: 1
});
}
The node title is split into multiple lines to fit the node’s width.The return parameter of this method is an object with two properties:
return {
text: // an array of strings that contains the text split into lines
lineHeight: // the height of a single line (different line sizes in are not supported
You can store this value (for example, in a closure) and then pass it to the renderText method described below.
If you want to change the node’s height or width, now is the best moment for that. Since the height property is used for the initial height of the node, we don’t want to change it directly, as it would grow indefinitely in multiple calls. Instead, you can use the method setSize, which sets internal variables and doesn’t change the height and width properties.
this.setSize({
height: Math.floor(oTitle.lineHeight * oTitle.text.length + oTrimmedText.lineHeight + 25 + 50 /*icon*/)
});
Other Methods Used for Rendering
To streamline the creation of SVGs, you can use several methods that are available for network graph nodes.
But, of course, you can always create your own custom SVGs without using these methods.
- computeTextDimensions – this method helps you split the text into lines. As mentioned above, SVG doesn’t support text wrapping, so you need to split it manually, specifying the text to be displayed on each line.
Important: This method should be called from the calculateSizes method.
- renderText – if you have already split your text into lines or if you just want to render one single line, you can use this method to render text as SVG. Please note, though, that this method won’t split text into
- renderIcon – this method provides an easier way to render an icon, using the SAP icon font.
- renderElement – this method provides a simple function wrapper for rendering a single SVG element.
We support two basic approaches to handling node customization. We start with the easier one – node’s content customization.
Partial Customization: Node’s Content Customization
Nodes with customized content may look like this. The basic shape is predefined but you can change the rest to fit your needs.
The Network Graph will still render the node’s wrappers that allow the network graph to handle status changes, hover events, and selection events as well as keyboard navigation. Which means you can focus on proper content positioning only.
To customize the node’s content, you need to override the method renderItemContent for each node you want to customize.
renderItemContent: function (mArguments) {
if (this.getShape() === "Custom") {
return Node.prototype.renderContent.call(this);
}
var sHtml = "";
if (this.getShape() === "Box") {
var y = 20;
if (this.oCustomTitleSize) {
sHtml += this.renderText({
x: 5,
y: y,
lineSize: this.oCustomTitleSize.lineHeight,
lines: this.oCustomTitleSize.text
});
y += this.oCustomTitleSize.lineHeight * this.oCustomTitleSize.text.length + 20;
if (this.oCustomTrimmedTextSize) {
// solo lane text
sHtml += this.renderText({
x: 5,
y: y,
lines: this.oCustomTrimmedTextSize.text,
attributes: {
"font-size": "20px"
}
});
y += this.oCustomTrimmedTextSize.lineHeight;
}
sHtml += this.renderIcon({
icon: this.getIcon(),
y: y + 30,
x: this.getWidth() / 2,
attributes: {
"text-anchor": "middle",
"font-size": "50px"
}
});
}
} else {
if (this.oCustomTitleSize) {
sHtml += this.renderText({
x: 30,
y: 35,
lineSize: this.oCustomTitleSize.lineHeight,
lines: this.oCustomTitleSize.text
});
}
}
return sHtml;
}
Then we can create an SVG string for the title, using the data we collected in the attached event calculateSizes.
As you can see, we can create different content, based on whether the node is of the circle or box type. We can use helper methods to render SVG strings and adjust the text size, usinge the object we created with the calculateSizes method.
This method requires a return string that represents an SVG element.
Complete Customization: Adjusting the Node’s Shape and Content
You can, however, customize your node completely, exploring other shapes beyond the standard box and circle. How about a diamond shape like the one pictured
If you want tocustomize both the node’s shape and its content, follow these steps:
- Set the node’s shape property to Custom
- Override the method renderContent for the node you want to customize.
renderContent: function (mArguments) {
// size determination rendering is for default nodes using max-width or multiline titles
// there is no need to waste time rendering in this cycle as its destroyed anyway
if (mArguments.sizeDetermination) {
return;
}
Important: The rendering function is called even for calculating the text length (with parameter sizeDetermination). Since we usually perform all text length calculations in a special event, it’s recommended that you skip the rendering in this case and save some computation time.
If you want the network graph to handle events for color changes, state changes, and hover states, you can do so by settings correct classes. For the top shape element, it’s sapSuiteUiCommonsNetworkInnerRect.
var FOCUS_OFFSET = 3;
var sHtml = "",
x = this.getX(),
y = this.getY(),
iHeight = 80,
iWidth = this.getWidth();
// set default classes if you want to have focus, hover and selected state handled by default
// otherwise you need to handle it yourself
sHtml += this.renderElement("path", {
stroke: "red",
"class": "sapSuiteUiCommonsNetworkInnerRect",
d: "M" + (x + iWidth / 2) + " " + y + " L " + (x + iWidth) + " " + ( y + iHeight / 2) +
" L " + (x + iWidth / 2) + " " + ( y + iHeight) +
" L " + (x) + " " + ( y + iHeight / 2) +
" L " + (x + iWidth / 2) + " " + ( y) + "Z"
});
This shape will be used for focus handling and keyboard navigation. If you want the network graph to take care of these, set the class to sapSuiteUiCommonsNetworkBoxFocus.
sHtml += this.renderElement("path", {
stroke: "red",
"class": "sapSuiteUiCommonsNetworkBoxFocus",
d: "M" + (x + iWidth / 2) + " " + (y + FOCUS_OFFSET) + " L " + (x + iWidth - FOCUS_OFFSET) + " " + ( y + iHeight / 2) +
" L " + (x + iWidth / 2) + " " + ( y + iHeight - FOCUS_OFFSET) +
" L " + (x + FOCUS_OFFSET) + " " + ( y + iHeight / 2) +
" L " + (x + iWidth / 2) + " " + ( y + FOCUS_OFFSET) + "Z"
});
Alternatively, you have can do this all in a different way, if it fits your needs better, in which case you don’t need to add the classes.
Finally, we can render the icon and the text that we split into lines earlier.
sHtml += this.renderIcon({
icon: this.getIcon(),
y: 65,
x: this.getWidth() / 2,
attributes: {
"text-anchor": "middle",
"font-size": "50px"
}
});
if (this.getTitle()) {
sHtml += this.renderText({
x: this.getWidth() / 2,
attributes: {
"text-anchor": "middle",
"pointer-events": "none"
},
y: iHeight + 20,
lineSize: oTitle.lineHeight,
lines: oTitle.text
});
}
Rendering of the Node’s Status Icon
Status icon is a part of the node’s shape.
If you override the renderItemContent method to change only the inner content of the node, the status icon will be rendered automatically, which means you needn’t do anything yourself. In case you still want to change the status icon, you can override the method renderStatusIcon. Please keep in mind that you will also have to take care of the colors for different states, such as node colors on select and on hover.
// render info box
sHtml += this.renderStatusIcon({
x: this.getWidth() / 2,
y: 0,
size: 15,
iconSize: 25
});
On the other hand, when you use complete customization, using the renderContent method, the status icon is not rendered. You can use the already mentioned method renderStatusIcon and call it directly (see the API reference for the available parameters) or create a completely different shape for this status icon. However, in this case you will again need to take care of the colors used for different node states.
In this tutorial, you’ve learned how to customize node shapes and their content in a network graph. For a more detailed example, see our sample page.
Great post! Just one question: Is it also possible to adjust the lines? I am looking for a way to add a label to each line.
Regards
Tizian
Hello, so far it is not. We will consider this in the future.
Just one suggestion: If it is only about adding some stuff (label etc) to the line, you may consider editing directly svg (i think best place would be in event graphReady). You can grab coordinate for every line, that can help you position your label properly.
Thanks for your feedback! I will check the approach but first I should have a look at the SVG documentation 😉
Hello Libor,
Could you tell how to add the custom color to the node. I am trying to set the rgb values to background color in status object but it is not taking the values.
Thanks,
Pooja
Hello,
what version you use?
The newest one should support that, as you can see on our explored example.
http://veui5infra.dhcp.wdf.sap.corp:8080/sapui5-sdk-dist/#/sample/sap.suite.ui.commons.sample.NetworkGraphCustomStatus/preview
best regards
Libor
Hello Libor,
Is it possible to control the circle node title position? It is always below the node, and I wanted to have it next to the node... can it be customized ?
Thanks a lot.
Isam
Hello it can be customized by two ways.
first you completely write your own HTML for circle nodes as show here:
https://sapui5.hana.ondemand.com/#/sample/sap.suite.ui.commons.sample.NetworkGraphCustomRendering/preview
or you can render standard not and in “afterrendering” manipulate your DOM to move the title next to the node. Keep in mind tho, that you can’t change the layouted data so nodes may overlap.
best regards
Libor
Hi - Thanks for the post. We were looking to build a network graph view but wanted to know if there is any possibility to create links between nodes as well OR is it only for visualization ?
Thanks & Regards
Srinivas Rao.
Hi Libor,
Thanks for sharing this blog. It would be very helpful if you kindly let me know how I can add multiple custom node shapes (i.e. Rhombus, Triangles etc.) and render them with custom color-statuses. The titles and node sizes would be standard in sizes.
Thanks Shubhranil Roy