Learn how GoJS enhances front-end development by creating interactive diagrams with flexibility and performance for complex data visualizations.
Our developers specialized in front-end solutions sometimes deal with initially written codes in the projects. It happens we notice poorly written code with the use of the GoJS elements. Therefore, in this article, we would like to explain what to be careful about in the case of insufficient knowledge of the front-end and what areas are worth a more profound understanding to avoid future code errors. It may result in much higher costs in the future.
We asked our three specialists for advice that might be useful to non-front-end experts. They distinguished four areas of expertise in which they most often encounter erroneous assumptions and solutions. We hope that they will help you understand the front-end better and avoid mistakes in the future.
Seperating GoJS code from front-end code
When writing an application using the GoJS library, paying attention to the code architecture is sound. Often, the diagram code becomes extensive, it can dominate the selected framework (e.g., React, Angular), and it would be a mistake to narrow it down to one / several files. I recommend that you separate it logically from the rest of the application and create an additional layer ensuring communication between the diagram and the rest of the frontend components. The diagram code should, as elsewhere in the project, consist of many small files, each with one responsibility - this makes it easier to reuse the functionality and enables the introduction of unit tests.
Node templates as the components
An important aspect is a style of creating templates for nodes and links. In the examples on the GoJS website, we can observe building objects in one function. From our experience, we have found the alternative approach and suggest the following:
Let's treat templates as components in our favorite front-end framework. Let's compose them from many small blocks; let's create divisions into components that are only used for presentation and those that have access to the application's state. The readability of such written objects is much more significant, and, similarly to the division into files, we enable the reuse of components elsewhere.
I also warn against abusing inheritance when creating object templates. At first glance, it seems like a good idea to create a base template and build the structure of more and more specialized objects on its basis. However, the requirements for nodes and links can drastically change over time, and the worst thing that can happen is when a simple change in one of the base classes spoils some of the templates. The inheritance structure may be large enough that extracting one template from it would be very time-consuming. Instead of inheritance, I recommend re-composing objects from small reusable blocks.
The power of GoJS functionalities
GoJS is an advanced library with a powerful arsenal of ready-made functionalities, which can often positively surprise the programmer - a feature that is difficult to implement may be almost immediately available in this library, requiring only a few lines of code to adapt to the project. The real trick, however, is to see such a solution. When creating advanced diagrams, remember to carefully read the library documentation - at least those areas you will use in the project. Knowledge of the GoJS API is a key condition for project implementation success - eye programming and checking documentation only when we get to the wall may turn out to be a disastrous practice. It is difficult to count how many times I have seen programmers losing all day to solve a problem that had an obvious source well described in the library documentation.
Accessing private object fields in GoJS
Developers who are just entering the world of the front-end must be cautious about the possibility of accessing private object fields in GoJS. Developers who are just entering the world of the front-end must be cautious about the possibility of accessing private object fields in GoJS. When looking for the right property and using the browser developer tools, you can come across several meaningless letter fields containing the information you are looking for. These are minified properties; their name will change with each new library version and absolutely cannot be used. It is enough to read the GoJS documentation on the topic of interest to find a public field with the appropriate value.
Overloading of tools in GoJS
The last topic I would like to mention is the overloading of tools in GoJS. It is a beneficial practice and forms the basis for the extensibility of this library. However, we should remember to design such an overload well. A naive approach in which we create a new instance of Tool and add all the code of the new logic directly to its code will only work in small projects. A better solution is to move the logic into a separate place and use only its public components. Thanks to this, we can register many distinct functionalities in the Tool code, which together will not create a spaghetti code.
The excellent architecture for the app lies in performing quality code. Let us tell you more about its importance and do developers need to always care about it.
What is quality code?
This question may sound a bit philosophical because everybody can somehow define what has quality or not. However, the quality of code has three distinct features:
Quality code can always be written. No matter what’s the language or technology. We know that the front-end is sometimes treated as a necessary evil, especially by developers who are forced to become full stacks. But it doesn’t mean that in JavaScript, we can’t write quality code.
The first step for quality front-end code is proper architecture, as we stated previously. The nice and clean division into smaller parts is an essential part of every development project. Since we’ve already written about it, let’s go further.
Clean code
Dividing the code leads to a bit broader topic, which is a clean code. The below acronyms describes the possible approach to maintain the clean code.
Code standards
Keeping to SOLID, DRY, and KISS is not everything that quality code has. Quality is not only about what code does and how it’s divided into specific code structures but also how the code looks. Indentations, way of starting blocks, and type of line endings are things that we may not always think of as the most necessary parts of the code while writing, but it’s an entirely different tale while reading.
Code should be treated like a traditional text. It should be formatted correctly, as any good text should be seperated into paragraphs and included formatting with headers It makes reading more comfortable. As for code, each programming language has a set of standard formatting rules, and organizations like to add some of their own.
It’s not different on a front-end. We have great tools that help with keeping your code formatted properly and standardized:
When JS doesn’t have any commonly agreed standard style, there are some popular and widely used. Just to name the most popular:
Just configure them in your project, and your code will look great without remembering to create proper indentations by hand.
Copying ready solutions
Despite clean code and code standards, you need to remember the proper approach to copying ready solutions from the Web. Copying the code from various databases or sources like StackOverflow may not always be a good approach. The same goes for GoJS. There are many samples on the provider's website, and they are written straightforwardly. Thus, they work for everyone and on every browser. But if your code will be transpiled, you can safely modernize it and apply your standards.
Treat code samples from the Web as an example of doing something, not as the only way to achieve something. Remember that it can be wrong. If you’d like to learn how copying code broke some software, including Docker, I recommend reading this Twitter thread.
Measuring code quality
Code quality can be measured. We have here a few aspects that we can measure:
Encountering low-quality code isn’t anything extraordinary. The challenge begins when you simply forget about some clever solutions and end up with nothing but a mess. With more experience, the developer should avoid harmful practices and put more effort into increasing code quality.
GoJS can be easily integrated with the most popular solutions like Angular, React, Vue, or any other you want. As usually in programming, there’s more than one way of achieving the goal. You can either use one of the dedicated packages (GoJS offers gojs-angular and gojs-react npm packages) or integrate by binding a specified div to the GoJS diagram. In this part of the article we are focusing on Angular as one of the popular frameworks, especially among the start-up and corporate clients. The aim is to present the idea of integrations on the Angular example.
Getting started with a gojs-angular package
To get your GoJS diagram rolling within your Angular app, simply install the gojs-angular package and import it into your module.
Then, in your component’s HTML file, use the provided <gojs-diagram> component and configure it in the TS file.
<gojs-diagram
[nodeDataArray]="diagramData?.nodes || []"
[linkDataArray]="diagramData?.links || []"
[modelData]="diagramData?.model || {}"
[initDiagram]="initDiagram"
[skipsDiagramUpdate]="skipsDiagramUpdate"
(modelChange)="diagramModelChange($event)"
></gojs-diagram>
Having done that, you have to set up the gojs-diagram component’s Inputs and Outputs to make sure that the diagram is rendered correctly. The component accepts a couple of Input/Output, of which some are mandatory.
InitDiagram is the method where a diagram is initiated. It must return the instance of go. Diagram object. Without it, the diagram won’t work at all.
private diagram: go.Diagram;
initDiagram = () => {
this.diagram = $(go.Diagram, { ... });
return this.diagram;
};
Integrate diagram model with your application state management
With the modelChange event emitter you can easily listen to any changes that has been applied to the diagram.
Let’s consider following scenario. There’s a service that is responsible for storing the diagram’s model. We want it to be always in sync with whatever is going on in our diagram.
To do that, we can utilize the power of the gojs-angular plugin and implement the functionality in the following way.
First, subscribe to our service’s data so that it’s our only source of truth.
ngOnInit() {
this.service
.getDiagramData()
.pipe(filter((x) => x != null))
.subscribe((state: DiagramState) => {
this.skipsDiagramUpdate = true;
this.diagramData = state;
});
}
Then, handle the changes on the diagram using the DataSyncService provided by gojs-angular package:
diagramModelChange(changes: go.IncrementalData) {
if (this.diagram == null || changes == null) return;
const newState = {
nodes: DataSyncService.syncNodeData(
changes,
this.diagramData.nodes,
this.diagram.model
),
links: DataSyncService.syncLinkData(
changes,
this.diagramData.links,
this.diagram.model as go.GraphLinksModel
)
model: DataSyncService.syncModelData(
changes,
this.diagramData.model
),
};
this.service.pushDiagramData(newState);
}
Now, the service controls the entire diagram state and is always in sync with all of the model changes.
Simple Integration
In some cases, you might not want to include additional packages into your code, or you’re dealing with very simple diagrams. In such scenarios, you can integrate GoJS into your application without any extra npm packages.
The way to achieve it is to use the old-fashioned binding of the GoJS to the desired div.
The below example pictures how you could do it in Angular
<div #diagramWrapper class="my-diagram"></div> export class GraphComponent implements AfterViewInit {
@ViewChild('diagramWrapper')
diagramWrapper: ElementRef<HTMLDivElement>;
ngAfterViewInit() {
if (!this.diagramWrapper) {
return;
}
this.setupDiagram();
}
private setupDiagram() {
const element = this.diagramWrapper.nativeElement;
this.diagram = new go.Diagram(element, {...});
}
}
GoJS offers a similar package for React (gojs-react), so alike complex integration is possible for the React Applications.
For now, there’s no library for Vue, however, GoJS can still be integrated with Vue using the simple integration way of binding the library directly to a given div element.
The GoJS is a very advanced diagram library, and therefore many graph problems are solved "out of the box." It provides the main algorithms for the layout of objects in 2d space, offers functions for transforming segments, points, sizes, and supports tree and hierarchical structures operations. This library does not free the programmer from thinking despite such extensive support, and working with more complex functionalities requires algorithmic basics.
Calculating the computational complexity of a function
An essential skill when working with diagrams is calculating the computational complexity of a function and striving for optimal implementation. In programming on the front-end, we sometimes accept functions with a complexity of x ^ 2 (square) because we usually operate on small data sets. It is different when working with graphs. Although initially, we have little data, very often with the following requirements, the number of displayed objects grows rapidly, and non-optimal functionalities become a significant bottleneck. I recommend that you think about pessimistic cases in advance and optimize functions, especially if the improvement is simple - e.g., using Set instead of an array.
On the other hand, one should be moderate, and when we are sure that simple implementation is sufficient and there is no threatening data set visible on the horizon, it is better to let go - optimization is a complex issue.
Recursion
Another element that requires caution is recursion. On the one hand, recursive functions are usually written more naturally than complex iterations. They are more readable, but on the other hand, remember that they are prone to a "stack overflow" error. Usually, this problem is ignored because we rarely operate on large enough data sets for the threat to be real. In diagrams, however, such situations happen, and they may even be frequent in some projects. Therefore, it is sometimes better to use stack iteration (an iterative simulation of recursion) instead of recursion. Also, note that hardly any browsers currently support tail recursion.
Graph traversal
Working with diagrams quite often, you need to traverse the graph. Knowing the basic DFS and BFS algorithms is very important in designing solutions. Sometimes, it is necessary to take care of the displayed nodes' appropriate order, and knowledge about topological sorting will help us. There may often be a requirement to determine various types of paths in the graph, and the knowledge of appropriate algorithms will benefit us (e.g., Dijkstra's algorithm). Basic programmers' knowledge of graph theory may be the essential factor in the success of a project, especially when the represented data set is large (at least several thousand objects).
Optimal representation of data in 2D space challenge
One of the most complex problems in diagram applications is the optimal representation of data in the 2D space. There are many types of layouts helpful in this, but we must choose them well for our data and the relationships in them. If the application data flows naturally into a tree structure, then, of course, the very quick TreeLayout solves this problem. The situation is different when we are dealing with a graph without the property of a tree. The complexity of algorithms that layout general graphs is pretty high, and it may not be possible to ensure that no edges and nodes intersect.
The use of LayeredDigraph and ForceDirected types of layouts
When dealing with an acyclic graph, we can use a LayeredDigraph type layout to represent it. On the other hand, if there are cycles in the graph, a ForceDirected layout may be a better choice. The implementation of both types of layouts is highly time-consuming and requires specialist algorithmic knowledge. To overcome this problem, libraries with ready-made implementations of algorithms are most often used. However, one should carefully study the properties of the implemented algorithm and its computational complexity to be sure that it will work well with our data. GoJS offers implementations of the algorithms mentioned above. Still, if we need more specialized layouts, there is nothing to prevent us from using an additional library and plugging it into the overload of the go. Layout class, thanks to which we can easily integrate it with GoJS.
Bearing in mind the above, when working on front-end applications, we can count on several positives, such as:
If you still feel that your understanding of the front-end is not sufficient, or you are lost in your work, consider hiring competent and experienced specialists or seek their advice.