Angular - Rete.js

Angular

This guide is an extension of the Basic guide and provides instructions for using the rete-angular-plugin instead of rete-react-plugin

BasicCustomizationControlsPluginAngularContext menuMinimapReroute

This plugin offers a classic preset that comes with visual components for nodes, connections, sockets, and input controls.

Compatible with Angular 12, 13, 14, 15 and 16

This plugin is exclusively designed for Angular applications as it requires Injector instance, unlike other render plugins.

Install dependencies

bash
npm i rete-angular-plugin rete-render-utils @angular/elements@16

Please note: this plugin relies on @angular/elements, which is based on Web Components. However, Web Components have a limitation - they cannot be unregistered. This limitation may result in the reuse of the initial Angular component instead of creating a new one when a node with the same identifier is added, potentially leading to the use of outdated data within a custom node, such as data from an injected service.

Plugin connection

This is an example of integration in Angular 16, but you can specify a different version (12, 13, 14, 15 or 16) in the import that matches the version of your application.

These versions have been compiled with Ivy.

ts
import { AreaPlugin } from "rete-area-plugin";
import { AngularPlugin, Presets, AngularArea2D } from "rete-angular-plugin/16";

type AreaExtra = AngularArea2D<Schemes>;

// ....

const render = new AngularPlugin<Schemes, AreaExtra>({ injector });

render.addPreset(Presets.classic.setup());

area.use(render);

where injector is an instance of Injector that can be obtained through dependency injection (DI).

Use Legacy View Engine

Additionally, the plugin provides support for the legacy engine which can be imported in the following way

ts
import { AngularPlugin, Presets, AngularArea2D } from "rete-angular-plugin";

Controls

This plugin provides built-in controls that are displayed based on the following objects:

  • ClassicPreset.InputControl as <input type="number" /> or <input type="text" />

Simply add the control to the node

ts
node.addControl('my-control', new ClassicPreset.InputControl("number", {
  initial: 0,
  readonly: false,
  change(value) { }
}))

If you want to add different types of controls, you can return the necessary component in the control handler of customize property.

tsx
import { ControlComponent } from "rete-angular-plugin/16";
import { MyButtonComponent } from './my-button.component'

render.addPreset(Presets.classic.setup({
  customize: {
    control(context) {
      if (context.payload.isButton) {
        return MyButtonComponent
      }
      if (context.payload instanceof ClassicPreset.InputControl) { // don't forget to explicitly specify the built-in ControlComponent
        return ControlComponent
      }
    }
  }
}));

node.addControl('my-button', { isButton: true, label: 'Click', onClick() {} })
ts
import { Component, Input } from "@angular/core";

@Component({
  selector: "app-button",
  template: `<button
    (pointerdown)="$event.stopPropagation()"
    (click)="data.onClick()"
  >
    {{ data.label }}
  </button>`
})
export class ButtonComponent {
  @Input() data!: { label: string, onClick: () => void };
}

This is a simplified version suitable for introductory purposes. For projects, it is recommended to follow the approach demonstrated in the example

Make sure to specify (pointerdown)="$event.stopPropagation()" to prevent the area from intercepting events such as click.

Customization

In a similar manner to the approach outlined above, you can replace node, connection, or socket components.

Customization of all nodes

If you want to completely change the node structure, you can implement your own component similar to node from the classic preset

ts
import { CustomNodeComponent } from './custom-node.component'

render.addPreset(Presets.classic.setup({
  customize: {
    node() {
      return CustomNodeComponent
    }
  }
}))

The implementation of CustomNodeComponent is available in the custom-node.component.ts file of the Customization for Angular example.

Specific nodes

You can add an extra condition to apply this component only to specific nodes.

ts
import { NodeComponent } from "rete-angular-plugin/16";

render.addPreset(Presets.classic.setup({
  customize: {
    node(context) {
      if (context.payload.label === "Custom") {
        return CustomNodeComponent;
      }
      return NodeComponent; // use built-in component
    }
  }
}))

await editor.addNode(new ClassicPreset.Node('White'))

Connection customization

Use connection as a starting point from the presets/classic/components directory of the plugin's source code.

ts
import { CustomConnectionComponent } from './custom-connection.component'

render.addPreset(Presets.classic.setup({
  customize: {
    connection() {
      return CustomConnectionComponent
    }
  }
}))

Socket customization

Use socket as a starting point from the presets/classic/components directory of the plugin's source code.

ts
import { CustomSocketComponent } from './custom-socket.component'

render.addPreset(Presets.classic.setup({
  customize: {
    socket() {
      return CustomSocketComponent
    }
  }
}))

Customize context menu

In order to customize the context menu for this rendering plugin, one can override styles using selectors (and it's important to consider the specificity of selectors in CSS)

scss
[rete-context-menu] {
  width: 320px;
  context-menu-search input.search {
    background: grey;
  }
  context-menu-item.block {
    background: grey;
  }
}

Other presets

Check out the complete result on the Customization for Angular example page.