We recommend using this tutorial together with Angular Reference.

Introduction

This start guide shows how to create an Angular application with a KeyLines chart.

For more information about integrating KeyLines with Angular, see also the Angular Integration demo and Angular Reference documentation.

Note that if you store KeyLines in your source control, you must ensure it's not publicly available. Read more about the Obfuscation Requirements before deploying your application.

Step 1: Create and serve an Angular app

If you already have an existing project with non-standalone components up and running, you can skip this step and continue adding KeyLines from Step 2, replacing my‑angular‑app with the name of your project's root directory and integrating the example code into your application code.

To start a new project, run these commands in the console to create the app:

npm install -g @angular/cli@v17
ng new my-angular-app --no-standalone
cd my-angular-app
ng serve --open
yarn global add @angular/cli@v17
ng new my-angular-app --no-standalone
cd my-angular-app
ng serve --open
pnpm install -g @angular/cli@v17
ng new my-angular-app --no-standalone
cd my-angular-app
ng serve --open

If you are prompted about any settings while the commands are running, you can accept the default values for now.

This process might take a while to run. You should now see a simple Angular app in your browser. In case of problems, see the Angular CLI documentation.

This tutorial uses Angular v17 and does not use Angular standalone components. To use them in this application, complete the tutorial and then use Angular's migration guide.

Step 2: Add KeyLines to the app

Download the KeyLines package from the link below and move it to the my-angular-app directory.

Request a trial (Required to Download)

In the terminal from my-angular-app, add KeyLines as a package dependency:

npm install file:./keylines-8.6.0-11643712880.tgz
yarn add ./keylines-8.6.0-11643712880.tgz
pnpm install file:./keylines-8.6.0-11643712880.tgz

Next, you need to copy the file that contains Angular-style declarations of KeyLines components into your Angular application.

Run the following command from inside the my-angular-app directory to copy the file to the correct location:

cp ./node_modules/keylines/angular/angular-keylines.ts ./src

Step 3: Create a chart

First, update src/styles.css to set the dimensions for the KeyLines chart:

html {
  box-sizing: border-box;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  margin: 0;
}

.chart-container {
  width: 100vw;
  height: 100vh;
}

Next, open src/app/app.module.ts, which is a root module that describes how the application parts fit together.

In this file, we will import the KeyLines module, declare the components in declarations and make KeyLines injectable into other components in providers.

Update the contents of this file to:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { KlComponent, KlComponents, KlComponentsService } from '../angular-keylines';   //  adds imports from angular-keylines.ts

@NgModule({
  declarations: [
    AppComponent,
    KlComponents,   // required wrapper component around KLComponent
    KlComponent     // KeyLines component containing the chart
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [KlComponentsService],   // makes KeyLines available in other components
  bootstrap: [AppComponent]
})
export class AppModule { }

Next, open src/app/app.component.html and replace its contents to inject the components into our template:

<kl-components (klReady)='klReady($event)'>   <!-- declared KlComponents wrapper -->
  <kl-component klType='chart' klContainerClass='chart-container'></kl-component>   <!-- declared KlComponent with a chart -->
</kl-components>

See more about kl-components and kl-component in the Angular Reference.

Finally, open src/app/app.component.ts, which is a controller for the KeyLines component that contains the logic controlling your chart.

In this tutorial, this file will also contain our data and data management, although in a real-world application, these would typically be separate from the controller.

Replace the code with a simple KeyLines chart:

import { Component } from '@angular/core';
import type { Chart, ChartData } from 'keylines/esm';   // imports TypeScript types from KeyLines

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title: string = 'my-angular-app';

  chart!: Chart;

  data: ChartData = {
    type: 'LinkChart',    // a KeyLines Chart type - must be 'LinkChart'
    items: [ { 
      id: 'id1',
      type: 'node',
      c: '#43976C',       // node colour
      t: 'Hello World',   // node text label
    } ]
  };

  klReady([chart]: [Chart]) {      // a function to run when the page is ready for the chart
    this.chart = chart;            // saves the chart reference
    this.chart.load(this.data);    // loads the data into the chart
    this.chart.layout();           // runs the default organic layout
  }
}

Once the chart component is created, the klReady function is called with a reference to the chart. The klready function saves the chart reference, loads the chart object using the chart.load() function and runs the layout using the chart.layout() function.

The chart data object passed to chart.load() loads a node with a green colour (c property) and a text label (t property).

The chart.layout() function by default runs the organic layout, which is a versatile force-directed layout algorithm that is especially great for large datasets and complex networks, but there are also other layouts you can explore.

At the end of this step, there should be no errors in your developer console or in your application and you should see a simple KeyLines chart in your browser:

A single green node with Hello World label

If you can't see a KeyLines chart in your browser, try closing the development server and running ng serve --open to launch it again.

Charts have a range of native options such as navigation to pan and zoom or overview window icon at the bottom right corner to toggle a chart overview.

Step 4: Add and parse the data

In app.component.ts, replace the existing data object inside the AppComponent class with this data:

data: Data = {
  person: [
    { name: 'Jenny', teamId: 1 },
    { name: 'Rosie', teamId: 0 },
    { name: 'Peter', teamId: 0 },
    { name: 'Sam', teamId: 1 },
  ],
  messages: [
    { from: 'Jenny', to: 'Sam', amount: 17 },
    { from: 'Sam', to: 'Jenny', amount: 12 },
    { from: 'Sam', to: 'Peter', amount: 1 },
    { from: 'Rosie', to: 'Jenny', amount: 2 },
    { from: 'Peter', to: 'Rosie', amount: 7 },
  ],
};

This dataset details messages between two teams of people.

We need to define types for this data. We will define them in the same file, although typically these would be in a separate file. Add the types just after imports at the top:

type Data = {
  person: { name: string; teamId: number }[];
  messages: { from: string; to: string; amount: number }[];
};

KeyLines accepts a ChartData object, which defines an array of chart items and their attributes. This means that we will need to parse our raw data to a KeyLines chart object. This is how we've defined our data model:

  • People are represented by nodes. Nodes require a type: 'node' and a unique id. The default node colour is transparent, so we will also set the c property.
  • Messages between people are represented as links. Links require a type: 'link', a unique id, and id1 and id2 properties whose values are ids of nodes at each link end.

To parse the data, add this function inside the AppComponent class:

parseData(data: Data): ChartData {
  const items: Array<Node | Link> = [];
  data.person.forEach(({ name }) => {         // defines people as nodes
    items.push({
      id: name,
      type: 'node',
      c: '#43976C',
    });
  });
  data.messages.forEach(({ from, to }) => {   // defines messages as links
    items.push({
      type: 'link',
      id: `link_${from}_${to}`,
      id1: from,
      id2: to,
    });
  });
  return { type: 'LinkChart', items };        // adds 'LinkChart' required by KeyLines
}

At the top of the file, update the list of imported KeyLines TypeScript types to contain the new types Node and Link:

import type { Chart, ChartData, Node, Link } from 'keylines/esm';

And finally, update the klReady function at the bottom of the file:

klReady([chart]: [Chart]) {                     // A function to run when the page is ready for the chart
  this.chart = chart;                           // saves the chart reference
  this.chart.load(this.parseData(this.data));   // loads the data into the chart
  this.chart.layout();                          // runs the default organic layout
}

Your application and developer console should now be clear of any errors and you should see this network in the browser:

A chart with four identical green connected nodes

Our data model is now visualised as nodes and links, but it's difficult to distinguish the individual items on the chart. To address this, we will incorporate details as styling in the next step.

Step 5: Add details with styling

KeyLines offers a variety of styling options for nodes and links. In our chart, we will use node and link styling as a way of showing item metadata.

To style the nodes, we will:

  • Use the c property to set a different colour for each team
  • Use the t property to add a text label and style it

To do this, update the first part of the parseData function where we have defined the people as nodes:

data.person.forEach(({ name, teamId }) => {
  items.push({
    id: name,
    type: 'node',
    c: [ '#43976C','#ff6f61' ][teamId],    // node colour by team
    t: { fbc: 'transparent', fs: 'auto', fc: 'white', t: name },    // white backgroundless text sized to fit the node
  });
});

To style the links, we will:

  • Use the w property to scale the link widths based on the number of messages sent
  • Use the a2 property to set a direction arrow that identifies the direction of the communication

To do this, update the second part of the parseData function where we have defined the messages as links:

data.messages.forEach(({ from, to, amount }) => {
  items.push({
    type: 'link',
    id1: from,
    id2: to,
    id: `link_${from}_${to}`,
    a2: true,    // arrow pointing to the recipient
    w: Math.pow(amount, 0.8)    // link width scalable by amount to the power of 0.8
  });
});

The chart now reveals which individuals belong to a specific group (team) and the strength of the relationship between the individuals (the amount of communication).

A chart with four nodes with two different colours, unique labels, connected by links with different widths

Step 6: Customise the chart options

Now let's customise some chart options. The chart options APIs can be used to change styling, display and interaction defaults of the chart as an element that integrates into your application.

Still in app.component.ts, add this object inside the AppComponent class:

chartOptions: ChartOptions = {
  selectedNode: { ha0: { c: "#f5bd1f", r: 37, w: 8 } },    // creates a halo around selected node 
  controlTheme: 'dark' ,    // turns navigation controls and overview window to dark mode
  backColour: '#1b2327',    // a background colour
};

Then update the list of imported KeyLines TypeScript types at the top of the page to contain the new type ChartOptions:

import type { Chart, ChartData, Node, Link, ChartOptions } from 'keylines/esm';

And in src/app/app.component.html, update the kl-component to include the klOptions object that will override the default component options with your new options:

<kl-components (klReady)='klReady($event)'>
  <kl-component klType='chart'
  klContainerClass='chart-container'
  [klOptions]='chartOptions'> <!-- object with options that override the default component options -->
  </kl-component>
</kl-components>

The klOptions object must be wrapped in [ ] punctuation as a data type that flows from source to view. See Types of Data Binding in the Angular documentation.

The resulting chart now looks like this when the 'Peter' node is selected:

A chart with dark background, dark mode navigation controls and a wide halo around the selected node

Step 7: Add event handling

In this step, let's add some code to respond when the user clicks an item on the chart.

KeyLines lets you respond to virtually any user event on the chart using chart events.

Add a click handler after the klReady function in src/app/app.component.ts:

klChartEvents( { name, args }: any) {
  if (name === 'click' && args) {
    console.log(`you clicked on ${args.id}`);
  }
}

And in src/app/app.component.html, update the kl-component to include the klEvents function that will attach KeyLines events for your component:

<kl-components (klReady)='klReady($event)'>
  <kl-component klType='chart'
  klContainerClass='chart-container'
  [klOptions]='chartOptions'
  (klEvents)='klChartEvents($event)'> <!-- function with KeyLines events attached for the component -->
  </kl-component>
</kl-components>

The klEvents function must be wrapped in ( ) punctuation as a data type that flows from view to source. See Types of Data Binding in the Angular documentation.

Try clicking chart items, navigation controls or the chart background. In the developer tools console you'll see that a string containing the id of the clicked item or navigation control is passed to the click handler, or null if you click on the chart background.

Step 8: Add foregrounding on selection

Now let's set a foregrounding behaviour that highlights the user-selected item and its immediate neighbours, which is particularly helpful for large charts with densely connected nodes.

Add the selection handler function inside the AppComponent class in src/app/app.component.ts:

selectionHandler() {
  const itemIds = this.chart.selection();
  const selectedItems = this.chart.getItem(itemIds); // Ensure we have clicked on a link or node, not the navigation control
  if (selectedItems.length) {
    const neighbours = this.chart.graph().neighbours(itemIds); // Foreground the selected item and its neighbours
    this.chart.foreground(
      (item) =>
        itemIds.includes(item.id) || neighbours.nodes.includes(item.id)
    );
  } else {
    // Clicked on background - restore all items to the foreground
    this.chart.foreground(() => true);
  }
}

And modify the click handler in the AppComponent to also include selection handler:

klChartEvents({ name, args }: any) {
  if (name === 'click' && args) {
    console.log(`you clicked on ${args.id}`);
  }
  if (name === 'selection-change') {
    this.selectionHandler();
  }
}

The new selectionHandler function uses chart.getItem to check whether the clicked id belongs to a node or a link in the chart. If it does, then we use chart.graph().neighbours() to find the nodes connected to the clicked item, and then call chart.foreground() to put them in the foreground, and other chart items in the background.

If the user clicked on something else, such as the chart background, then we foreground all the chart items.

A chart where only the selected node and its neighbours are foregrounded

Next steps

This is just the beginning of what KeyLines can offer.

You can continue reading about the features from this tutorial in Asynchronous JavaScript, Chart Basics, Layout Basics and Events Basics.

Have a look also at our Demos and our API Reference, or visit our blog for interesting articles about graph visualisation.

Here's a few suggestions on where to go next if you're interested in...