Effective front-end development with GoJS

Effective front-end development with GoJS

Synergy Codes

|
31 min
|
Effective front-end development with GoJS

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. 

How to deal with the lack of front-end code architecture in the GoJS app? – by Łukasz Jaźwa, Software Consultant  

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. 

Real-time data in diagrams
Real-time data on the diagram for IoT solutions in manufacturing 
Diagrams for measuring database quality
Entity diagram for a healthcare company 
Simultaneous collaboration tool with real-time actions.
Multiuser interaction diagram for real-time work  
Electric circuit flow diagram
 Electric circuit modeling and simulation diagrams 

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.  

How to take care of the code quality? – by Tomek Świstak Senior JavaScript Developer 

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:   

  • There are principles.  
  • There are standards.  
  • It can be measured.  

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.   

  • SOLID – these are five principles proposed by Robert C. Martin that describe how object-oriented code should be written. However, these rules are so universal that one can also apply them to any other paradigm. For example, the Single Responsibility Principle is, in my opinion, a basic for everything code-related. If you’ve ever created a single JavaScript function that does many things inside, it’s against that principle.  
  • DRY (Don’t Repeat Yourself) – this principle tells us that we should reuse the code and never copy-paste it from one place to another. It can simplify reading the code later a lot. Also, if you spot a bug in your code, you have just one place to fix it.   
    By the way, violations of DRY are called WET (Write Every Time/Waste Everyone’s Time). I love this naming.  
  • KISS (Keep It Simple Stupid) — this one is not strictly code-related (originated by US Navy), but the name tells everything. Don’t overcomplicate things. Do everything as simple as possible. Don’t reinvent the wheel.  

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:  

  • ESLint – checks for code quality issues and can fix some of them.  
  • Prettier – automatically formats code to the perfect form.  
  • StyleLint – the same as ESLint, but for CSS stylesheets.  
  • EditorConfig – this tool is general for any text files and can help keep consistent basic styles in a project. Remember, to always have it installed when working in a shared codebase! 

When JS doesn’t have any commonly agreed standard style, there are some popular and widely used. Just to name the most popular:  

  • ESLint Recommended   

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:  

  • Reliability – tells how long software runs without a failure.  
  • Maintainability – how easy it is to maintain the software. Generally, it’s hard to measure it, but using tools like ESLint, the number of warnings can be a good metric. In this case, the less, the better.  
  • Testability – how software supports testing. The metric here would be how many test cases you need to test the software thoroughly and find potential bugs. From a developer’s point of view, think about unit tests. How many must you write to test something properly? It somehow goes back to SOLID principles because if e.g., your function has only one responsibility, unit tests of it would be simple – therefore, testability is higher.  
  • Portability – is your software platform independent? It’s significant for front-end developers because many devices can run web browsers: computers, tablets, smartphones, TVs, game consoles, e-book readers. You should consider it from the beginning. If you know that your software will also be used on mobile phones, develop it like this from the start. More the code (or stylesheets) there is in the app, the harder it is to support other devices.   
  • Reusability – is it possible to reuse your code. It simply goes back to the DRY principle. Some tools can measure how many fragments in your code are duplicated (e.g. it’s built in IDEs by JetBrains)   

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 integration with frameworks and JS libraries  – by Kasia Biernat, Senior Angular Developer 

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;  

  };  
  • nodeDataArray - An array containing data objects for your nodes.  If there are no nodes at the beginning, the array could be empty.  
  • linkDataArray - An array containing data objects for your links. This Input is optional, so don’t worry of you don’t want to put any links into your diagram  
  • modelData - A data object, containing your diagram’s model.modelData. Model data is useful to help with the templates bindings, however, this input is also optional, so no need to worry about it before you actually need it.  
  • skipsDiagramUpdate - A boolean flag, specifying whether the component should skip updating, often set when updating state from a GoJS model change. It’s handy when you connect your diagram state to app state management system.  
  • modelChange –The Event Emitter (Output) that will emit a new value every time there are changes to the Diagram’s model (e.g. links are created or changed, nodes are added to the diagram etc.). The emitted value type is go.IncrementalData, which contains the list of changes that occurred on the diagram. Subscribing to this Event Emitter allows you to sync up the model with your app state management system to make sure that the GoJS diagram model is in sync with the app in-memory model.  

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 algorithms in GoJS – by Łukasz Jaźwa, Software Consultant    

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

Summary 

Bearing in mind the above, when working on front-end applications, we can count on several positives, such as: 

  • Possibility of further and more accessible product development 
  • Savings in time and money 
  • Scale the product as needed 

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.