A minimal Angular application that renders an empty yFiles graph canvas using a Service + Dependency Injection architecture that all subsequent chapters build on. No data yet — but the canvas will be fully interactive: you can pan by dragging and zoom with the scroll wheel.
- Node.js 18+ and npm
- A yFiles for HTML license (a
.tgzpackage file and alicense.jsonfile). If you are evaluating yFiles, download the evaluation package from the yWorks Customer Center.
Create a new Angular project with the Angular CLI:
npm create @angular my-yfiles-app
cd my-yfiles-appThe generated project gives you a standard Angular + TypeScript setup.
yFiles for HTML is distributed as a local npm package, a .tgz file. See the Working with the yFiles npm Module Developer's Guide section for in-depth information.
If the yFiles dependency has not been set up yet using the toplevel npm workspace, install it:
# Enter the correct path to your yFiles tgz found in your extracted yFiles for HTML package
npm install ./path/to/yfiles-<yFilesVersion>+dev.tgzHere, we use the development version of the library. Again, the Developer's Guide provides more in-depth information in the Development Mode chapter.
After installation, the yFiles TypeScript types are available in node_modules
and autocompletion works in your IDE just like any other npm package.
yFiles requires a valid license at runtime. Without one, the library will throw an error before rendering anything.
The tutorial apps expect the license.json in the src folder.
The license is loaded by assigning it to License.value before any other
yFiles API is called:
import { License } from '@yfiles/yfiles'
import licenseData from './license.json'
License.value = licenseDataSee also the Developer's Guide section on Licensing.
4. The GraphComponent
GraphComponent is the central UI element in yFiles. It is not an Angular
component; it is a plain DOM element (a <div> containing an <svg> canvas)
managed entirely by yFiles. yFiles uses its own rendering engine rather than
Angular's change detection.
This means you need to bridge the two worlds: let Angular manage a container
<div>, and let yFiles own a child element inside it.
Rather than creating the GraphComponent inside a single component, we use
Angular's dependency injection system so any component in the app can
access it without threading it through @Input() properties.
Create src/app/graph-component.service.ts:
import { Injectable } from '@angular/core'
import { GraphComponent, GraphViewerInputMode } from '@yfiles/yfiles'
@Injectable({ providedIn: 'root' })
export class GraphComponentService {
private graphComponent!: GraphComponent
getGraphComponent(): GraphComponent {
if (!this.graphComponent) {
this.graphComponent = new GraphComponent()
// GraphViewerInputMode enables panning, zooming, and item selection,
// but prevents users from creating or editing graph elements.
this.graphComponent.inputMode = new GraphViewerInputMode()
}
return this.graphComponent
}
}@Injectable({ providedIn: 'root' }) registers the service in the root
injector, making it a singleton across the entire application. Any
component that injects GraphComponentService receives the same instance.
getGraphComponent() creates the GraphComponent on the first call and
returns the same instance on every subsequent call. The factory runs once
and the result is reused.
Creating GraphComponent just allocates memory — it does not need DOM access.
We initialize it lazily rather than in the constructor so that it is only
created when actually needed.
Setting inputMode = new GraphViewerInputMode() enables panning, zooming,
and item selection while preventing users from creating or editing graph
elements. Always use a viewer input mode when displaying read-only data;
without it, the default GraphEditorInputMode lets users drag nodes and draw
edges.
Create src/app/graph-view/graph-view.component.ts:
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'
import { GraphComponentService } from '../graph-component.service'
@Component({
selector: 'app-graph-view',
templateUrl: './graph-view.component.html',
styleUrl: './graph-view.component.css',
})
export class GraphViewComponent implements AfterViewInit {
@ViewChild('graphContainer') graphContainerRef!: ElementRef<HTMLDivElement>
constructor(private graphComponentService: GraphComponentService) {}
ngAfterViewInit(): void {
const graphComponent = this.graphComponentService.getGraphComponent()
const div = graphComponent.htmlElement
div.style.width = '100%'
div.style.height = '100%'
this.graphContainerRef.nativeElement.appendChild(div)
}
}The template (graph-view.component.html) provides the container:
<div #graphContainer style="width: 100%; height: 100%;"></div>ngOnInit fires when the component is initialized but before the template
is rendered. The @ViewChild reference (graphContainerRef) points to a
template element that does not exist yet at that point — it would be undefined.
ngAfterViewInit fires after Angular has rendered the template and all
@ViewChild references are populated. DOM manipulation must happen here.
AppComponent imports GraphViewComponent and renders it:
// src/app/app.component.ts
@Component({
selector: 'app-root',
template: '<app-graph-view></app-graph-view>',
styles: [':host { display: block; width: 100vw; height: 100vh; overflow: hidden; }'],
imports: [GraphViewComponent],
})
export class AppComponent {}AppComponent never touches GraphComponent directly — it composes
GraphViewComponent and lets it handle the yFiles integration.
The :host style sets the app to fill the entire viewport. yFiles sizes its
canvas based on the container dimensions, so an explicit size is required.
npm startOpen http://localhost:4200. You should see a blank white canvas. Try panning
(click and drag) and zooming (scroll wheel) — yFiles enables both by default.
| Concept | Summary |
|---|---|
License.value |
Must be set before any yFiles API call. |
GraphComponent |
The yFiles GraphComponent is the main view component that visualizes a graph instance. |
GraphComponentService |
Singleton service that owns the GraphComponent. Shared via Angular's dependency injection. |
@Injectable({ providedIn: 'root' }) |
Registers the service as a singleton in the root injector. |
@ViewChild |
Gives access to a template element reference by name. |
ngAfterViewInit() |
Lifecycle hook that fires after the template is rendered. Use for DOM access. |
GraphViewerInputMode |
Read-only interaction: pan, zoom, select. No graph editing. |
Chapter 2: Displaying a Graph →
In the next chapter we'll use the IGraph API to create nodes and edges
programmatically, and apply a layout algorithm to arrange them automatically.