Skip to content

Getting Started

Jan Koehnlein edited this page Feb 8, 2019 · 2 revisions

The main steps to obtain a Sprotty diagram are the following:

  1. Define your model by creating subclasses of SModelElement.
  2. Define the SVG to generate for each type of model element by implementing views.
  3. Configure the diagram through dependency injection.
  4. Connect to a model source, either local or remote

Define the Model

Sprotty comes with a set of model classes that you can reuse for your application, e.g. SNode and SEdge for graphs and SChildElement for other views. However, it is often necessary to add application-specific properties to model elements, so their graphical views can be parameterized. For example, the following code defines a schema interface and a model class:

interface TaskNodeSchema extends SNodeSchema {
    name?: string
    status?: string
}

class TaskNode extends SNode {
    name: string = ''
    status: string = ''
}

The TaskNodeSchema is used to feed model contents into the Sprotty application, while the TaskNode represents the elements inside the application. You can skip the schema type if the models are created on the server (in that case you need a server-side representation of the JSON schema).

Define the View

A view maps a model element to its graphical representation. In the following example we use the JSX syntax to create an SVG group with a circle and a text element.

@injectable()
export class TaskNodeView implements IView {
    render(node: Readonly<TaskNode>, context: RenderingContext): VNode {
        const radius = 20;
        // In this context, the coordinates (0,0) mark the upper left corner of
        // the node, thus we shift all elements by the radius of the circle.
        return <g>
            <circle class-sprotty-node={true} class-task={true}
                    class-running={node.status === 'running'}
                    class-finished={node.status === 'finished'}
                    r={radius} cx={radius} cy={radius}></circle>
            <text x={radius} y={radius + 5}>{node.name}</text>
        </g>;
    }
}

The SVG elements are styled with CSS:

.sprotty-node.task {
    fill: #c0e0fc;
    stroke: #444;
    stroke-width: 1;
}

.sprotty-node.task.running {
    fill: #f00;
}

.sprotty-node.task.finished {
    fill: #0f0;
}

text {
    stroke-width: 0;
    stroke: #000;
    fill: #000;
    font-family: sans-serif;
    font-size: 11pt;
    text-anchor: middle;
}

Configure your Application

As described in Dependency Injection, the configuration of a Sprotty application is done using InversifyJS. We recommend to define your InversifyJS Container in a file named di.config.ts, which could look like this:

const flowModule = new ContainerModule((bind, unbind, isBound, rebind) => {
    rebind(TYPES.IModelFactory).to(SGraphFactory).inSingletonScope();
    const context = { bind, unbind, isBound, rebind };
    configureModelElement(context, 'graph', SGraph, SGraphView);
    configureModelElement(context, 'task', TaskNode, TaskNodeView);
    configureModelElement(context, 'edge', SEdge, PolylineEdgeView);
});

const container = new Container();
container.load(defaultModule, selectModule, moveModule, boundsModule, undoRedoModule, viewportModule,
        exportModule, updateModule, graphModule, routingModule, flowModule);
export default container;

With container.load(...) you can include all Sprotty modules that are required by your application. Then the view classes are assigned to model element types using the ViewRegistry.

Connect the Model Source

Sprotty supports two kinds of model sources:

  • LocalModelSource allows to create models directly in TypeScript or JavaScript.
  • WebSocketDiagramServer delegates to a remote source that is connected via web socket.

In this guide we consider the local variant. Add the following line to your ContainerModule (named flowModule in the example configuration above) in order to enable the model source:

bind(TYPES.ModelSource).to(LocalModelSource).inSingletonScope()

Afterwards you can use the LocalModelSource to initialize and update the model. For example, the following code creates a graph with two task nodes connected with an edge:

const graph: SGraphSchema = {
    type: 'graph',
    id: 'root',
    children: [
        {
            type: 'task',
            id: 'task01',
            name: 'First Task',
            status: 'finished'
        } as TaskNodeSchema,
        {
            type: 'task',
            id: 'task02',
            name: 'Second Task',
            status: 'running'
        } as TaskNodeSchema,
        {
            type: 'edge',
            id: 'edge01',
            sourceId: 'task01',
            targetId: 'task02'
        } as SEdgeSchema
    ]
}

const modelSource = container.get<LocalModelSource>(TYPES.ModelSource)
modelSource.setModel(graph)