Skip to main content

Views

Views are used to define the visual appearance of the various artifacts (which template to use for Nodes/Groups/Ports, CSS classes, etc) and event registration. If you want to read about defining connectivity rules, see the Data Model documentation.

When using vanilla Toolkit (no library integrations), views are provided as an argument to the render method of an instance of the Toolkit. When using a library integration views are specified separately from render parameters.

Example#

This example is an edited View from the Database Visualizer example application.


import { AnchorLocations } from "@jsplumb/common"import { LabelOverlay, DotEndpoint } from "@jsplumb/core"import { EVENT_CLICK } from "@jsplumbtoolkit/browser-ui"import { StateMachineConnector } from "@jsplumb/connector-bezier" 
toolkit.render(someTargetElement, {  view:{    // we have two node types - 'table' and 'view'.      nodes:{      "table":{        // we use 'tmplTable' to render tables        templateId:"tmplTable"      },      "view":{        // and 'tmplView' to render views        templateId:"tmplView",        events:{          // when you click a view Node, we alert its id.          [EVENT_CLICK]:(params:{node:Node}) => {            alert("click on view " + params.node.id);          }        }      }    },    edges:{      // common appearance of all edges      "common":{        connector:StateMachineConnector.type,        paintStyle:{ lineWidth:2, strokeStyle:"#CCC" }       },      // a 1:1 relationship      "1:1":{        parent:"common",  // declared 'common' as its parent.        overlays:[          { type:LabelOverlay.type, options:{ label:"1", location:0.1 } },          { type:LabelOverlay.type, options:{ label:"1", location:0.9 } }        ]      },      "1:N":{        parent:"common",        overlays:[          { type:LabelOverlay.type, options:{ label:"1", location:0.1 }},          { type:LabelOverlay.type, options:{ label:"N", location:0.9 }}        ]      }    },    ports:{      // a table has an arbitrary number of columns; it is a table's columns that actually connect to other tables, not a table itself.      "column":{        // use 'tmplColumn' to render a Port of type 'column'        templateId:"tmplColumn",        // the appearance of the endpoint on a column        endpoint:{type:DotEndpoint.type, options:{ radius:7 } },        // anchor locations on a column        anchor:[ AnchorLocations.Left, AnchorLocations.Right ],        // the type of edge that will be created from this port by default when the user drags a connection        edgeType:"common"      }    }  }})

Mapping types#

An instance of the Toolkit has the concept of a typeFunction - a function that, when given the data corresponding to some Node, Port, Edge or Group, returns the object's type (as a string). The type returned by this function is what is used as the key into a View.

Default Types#

For each of Nodes, Groups, Ports and Edges, you can provide a default definition, using the key "default":

{  ...  view:{    nodes:{      "default":{        templateId:"tmplNode"      }    }  },  ...}

This definition will be used in two circumstances:

  • no type was derived for some object by the current typeFunction
  • a type was derived but there is no entry in the View for that type

For convenience, @jsplumb/common exports a constant DEFAULT whose value is "default".

Default Edge Type#

If you do not specify an edgeType parameter on a Port definition, the type will be set to default.


Definition Inheritance#

Any Node, Port, Edge or Group definition can declare a parent. The resulting definition consists of the parent's entries, with the child's entries merged on top. An example:

{    ...    view:{      nodes:{        "common":{          events:{            [EVENT_CLICK]:(params:{node:Node}) => {              console.log("Click on node", params.node);            }          }        },        bigNode:{          parent:"common",          templateId:"tmplBigNode"        },        smallNode:{          parent:"common",          templateId:"tmplSmallNode"        }      }    },    ...}

Here, we have defined a common click handler on the parent definition, and then defined templates for each Node type in their own definitions.


Nodes#

There are two node types defined in the example above - table and view. They each have a single entry that defines the template to use to render a node of this type. The value for this parameter must be the id of some client side template in the format that is appropriate for the template engine you are using. See below for a discussion of templates.

In addition, the view Node declares an event handler for Node clicks. There are many different events to which you can subscribe; these are discussed below.

Supported Parameters#

  • parent ID of a Node definition from which this definition should inherit properties.
  • templateId ID of the template to use to render the Node. This applies only when you are using "vanilla" jsPlumb Toolkit, without a library integration such as Angular, React or Vue.
  • template Content of the template to use to render the node. In some situations, if a template is small, it can be convenient just to supply it to the view. Note that if you supply this you should not supply also templateId; if you do also supply templateId, template will be ignored.
  • events JS object containing mappings of event names to handler functions
  • component For use in Angular, React or Vue integration. Specifies the class of some component to use to render the given model object.
  • jsx For use in React, as an alternative to component. See the React Integration documentation for more information.

Edges#

In the Database Visualizer, an edge maps the concept of a relationship between two tables. This View shows three entries in the edges section. The first, common, contains directives that are common to all of the different relationships.

The second and third entries - 1:1 and 1:N - are concrete edge types, defining a 1:1 and a 1:N relationship respectively. Notice how they both declare their parent to be common, so they derive the styling directives from that type. Then each one declares two overlays that are unique to itself.

edges:{      // common appearance of all edges      "common":{        connector:StateMachineConnector.type,        paintStyle:{ lineWidth:2, strokeStyle:"#CCC" }       },      // a 1:1 relationship      "1:1":{        parent:"common",  // declared 'common' as its parent.        overlays:[          { type:LabelOverlay.type, options:{ label:"1", location:0.1 } },          { type:LabelOverlay.type, options:{ label:"1", location:0.9 } }        ]      },      "1:N":{        parent:"common",        overlays:[          { type:LabelOverlay.type, options:{ label:"1", location:0.1 }},          { type:LabelOverlay.type, options:{ label:"N", location:0.9 }}        ]      }    },

There is no limit to the depth of inheritance supported, but multiple inheritance is not supported. In the example above we could have actually defined some other abstract type, 1:something :

edges:{
   ...common...      "1:something":{     parent:"common",     overlays:[       { type:"Label", options:{label:"1", location:0.1 }}     ]   },   "1:1":{     parent:"1:something",     overlays:[       { type:"Label", options:{label:"1", location:0.9 }}     ]   },   "1:N":{     parent:"1:something",     overlays:[       { type:"Label", options:{label:"N", location:0.9 }}     ]   }}

...which has the overlay at 0.1 that both of the other edge types had previously independently declared. It all depends on how complex you want or need to be.

Note in the Database Visualizer app, there is actually a third edge type, not shown here: N:M.

Supported Parameters#

All parameters are optional.

  • parent ID of an Edge definition from which this definition should inherit properties.
  • connector Name/definition of the jsPlumb connector to use. If you omit this, the default jsPlumb connector will be used.
  • paintStyle Definition of the paint style to use. If you omit this, the default jsPlumb paint style will be used.
  • hoverPaintStyle Definition of the hover paint style to use. If you omit this, the default jsPlumb hover paint style will be used (which is null, unless you have set it otherwise).
  • events JS object containing mappings of event names to handler functions
  • label Either a plain string or a parameterised string (eg "${someValue}"). If present, a Label overlay will be created using this value. By default, this overlay will be at location 0.5 (halfway along the path of the edge), but this can be changed by setting a value for labelLocation in the backing data for an edge.
  • labelLocationAttribute Optional. If defined, nominates the name of the attribute within each edge's backing data from which to derive the location of the label overlay. The default value of this parameter is, effectively, labelLocation, as discussed above.

Ports#

This View shows one type of Port - a column. Recall from the data model discussion that a Node has one implicit Port (itself), and an arbitrary number of other Ports. In the case of the Database Visualizer, a Node represents a table, and a Port represents a column. The table's "implicit" Port is not used in the Database Visualizer application. In the Flowchart Builder, each object type's implicit Port is used when the object is the target of some Edge.

As with Nodes, Ports can be associated with a template for rendering. they do no have to be - in some cases, you will want to use an Endpoint to represent a Port, and in others you'll want to use a DOM element. If you're using DOM elements and your application supports the dynamic addition of new Ports - as in the case of the Database Visualizer when the user adds a new column to a table - then you need to provide the Toolkit with a separate template to use to render the Port (as opposed to rendering the Ports inside of each Node's template).

Port definitions are a description of the appearance and behaviour of the endpoints associated with the Port. Here we are using a Dot endpoint of radius 7, and instructing the Toolkit to use a dynamic anchor with Left and Right locations.

Specifying Endpoints vs DOM elements#

When jsPlumb encounters a jtk-endpoint element in a template, it knows that you wish to use an Endpoint to show the Port, as opposed to using a DOM element. If, though, you add a Port programmatically to some Node, and you wish to show that as an Endpoint, you need to tell jsPlumb, in the appropriate Port definition:

view:{  ...  ports:{    "someEndpointPort":{      isEndpoint:true      ...    }  }}

Limiting Connections#

A Port may support an arbitrary number of Edges. You can limit this with the maxConnections argument in the Port definition, but note that you would ideally set this in the Toolkit's model rather than in a View, as setting it here will set it only for the current renderer.

The default maximum - which is jsPlumb's default - maximum is 1 edge. A value of -1 means there is no upper limit.

Edge Type#

Notice in the column Port there is an edgeType parameter. This tells the Toolkit what type of Edge to create when a new connection is dragged from the Port. In this example we are using the type common, because when the user drags a new relationship between two tables we do not yet know the cardinality of the relationship. The Database Visualizer subscribes to the edgeAdded event from the Toolkit and prompts the user to specify the cardinality of the new Edge; the Edge is then assigned the appropriate type.

Dragging Connections#

As mentioned above, a Port is synonymous with the concept of an Endpoint in jsPlumb. By default, Endpoints in jsPlumb do not have mouse support enabled. You need to indicate whether or not the Endpoint should be allowed to be the source and/or target of connections created with the mouse through the use of the source/target parameters.

Supported Parameters#

  • parent ID of a Port definition from which this definition should inherit properties.
  • templateId ID of the template to use to render the port
  • edgeType Type of Edge to create when a new connection is dragged from the Port
  • maxConnections Defaults to 1. The maximum number of connections the Port allows. Set to -1 to allow unlimited connections.
  • events JS object containing mappings of event names to handler functions
  • source Defaults to false. If true, indicates the Port can be a source for Edges dragged with the mouse.
  • target Defaults to false. If true, indicates the Port can be a target for Edges dragged with the mouse.
  • isEndpoint Instructs jsPlumb that any Ports of this type that are programmatically added to a Node should result in the addition of an Endpoint to the UI. Defaults to false, meaning that Ports added programmatically configure the entire element as a source or target.

Groups#

For a full discussion on Groups, including how to map them inside Views, take a look at this page.


Render events#

Each type of object in the View supports declarative registration of events, through the inclusion of an events member in the appropriate definition. The list of supported events is as follows:

  • tap
  • dbltap
  • click
  • dblclick
  • mousedown
  • mouseup
  • mouseout
  • mouseover
  • contextmenu
view:{  nodes:{    someNodeType:{      events:{        click:(params) => {          // params.toolkit is the Toolkit instance          // params.renderer is the Surface widget          // params.e is the original mouse event           // params.node is the Toolkit Node          // params.el is the element from the DOM representing the Node        }      }    }  },  edges:{    someEdgeType:{      events:{        mouseover:(params) => {          // params.toolkit is the Toolkit instance          // params.renderer is the Surface widget          // params.e is the original mouse event          // params.edge is the Toolkit Edge          // params.connection is a jsPlumb Connection object.          console.dir(params.edge, params.connection);        }      }    }  },  ports:{    somePortType:{      events:{        mousedown:(params) => {          // params.toolkit is the Toolkit instance          // params.renderer is the Surface widget          // params.e is the original mouse event          // params.port is the Toolkit Port          // params.endpoint is a jsPlumb Endpoint object          console.dir(params.port, params.endpoint);        }      }    }  }}

Interceptors#

Interceptors are events that can be used to cancel some proposed activity. The Toolkit currently supports two of these:

  • beforeDrop Called when the user has attempted to drop a connection. Returning anything other than true aborts the connection.
  • beforeDetach Called when the user has attempted to detach a connection. Returning anything other than true aborts the detachment of the connection.

You can map these in the View by including an interceptors object. They are only supported on Port definitions, as they are functionality that is handled by the underlying Endpoints.

An example:

var tk = jsPlumbToolkitBrowserUIVanilla.newInstance();tk.render(document.getElementById("foo"), {  view:{    ...    ports:{      "default":{        templateId:"tmplFoo",        interceptors:{          beforeDrop:function(params) {            // returning anything but true will cause the connection to be aborted.          },          beforeDetach:function(params) {            // returning anything but true will cause the detach to be aborted.          }        }      }    }  }});

The contents of params is as follows:

  • connection associated jsPlumb connection
  • scope scope of the edge
  • source source info
    • el:source DOM element
    • id:id of the source Node or Port.
    • obj: Source Node or Port
    • type:"Node" or "Port"
  • target target info
    • el:target DOM element
    • id:id of the target Node or Port.
    • obj: Target Node or Port
    • type:"Node" or "Port"

Preconfigured Parameters#

Note Preconfigured parameters are known to break 2-way data binding when using the Toolkit with AngularJS, because the data object passed to the template is a copy of the original. To switch off preconfigured parameters, set enhancedview:false in your render call. With the other library integrations - Angular, Vue and React - the Toolkit takes care of setting this flag appropriately.

You may have a template that you'd like to reuse for a collection of nodes, with slight variations between each. Consider this example:

<script type="jtk" id="tmplRectangle-svg">  <svg style="position:absolute;left:0;top:0;" version="1.1" xmlns="http://www.w3.org/1999/xhtml">    <rect width="${width}" height="${height}" x="${strokeWidth}" y="${strokeWidth}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" {{if rotate}}transform="rotate(${rotate} ${(width / 2) + strokeWidth} ${(height/2) + strokeWidth})"{{/if}}/>  </svg></script>

This template draws SVG rectangles, of any given size, with arbitrary fill and stroke, and possibly rotated. Let's say we're creating an application in which there are two types of rectangle shapes: big red ones, and little yellow ones. In our View we can define custom parameters for each of these node types, which will be mixed in with the node's data when it comes time to render:

view:{  nodes:{    "bigRed":{      templateId:"tmplRectangle",      parameters:{        width:250,        height:250,        fill:"red"      }    },    "smallYellow":{      templateId:"tmplRectangle",      parameters:{        width:50,        height:50,        fill:"yellow"      }    }  }}

Preconfigured Parameters & Inheritance#

Preconfigured parameters are merged when one definition inherits from another, but only to the first level. Consider this arrangement:

view:{  nodes:{    "base":{      templateId:"tmplRectangle",      parameters:{        lineWidth:2,        foo:{          bar:"baz"        }      }    },    "bigRed":{      parent:"base",      parameters:{        width:250,        height:250,        fill:"red",        foo:{          qux:"FOO"        }      }    },    "smallYellow":{      parent:"base",      parameters:{        width:50,        height:50,        fill:"yellow"      }    }  }}

Note the foo parameter is declared in all three definitions. After merging, the two concrete node definitions have these values:

"bigRed":{  parent:"base",  parameters:{    width:250,    height:250,    fill:"red",    foo:{      qux:"baz"    }  }}
"smallYellow":{  parent:"base",  parameters:{    width:50,    height:50,    fill:"yellow",    foo:{      bar:"baz"    }  }}

So the foo entry in bigRed would completely overwrite the foo entry from base; the two are not merged together. smallYellow does not have foo entry and therefore inherits it from base.

Note Preconfigured parameters operate at the view level only: they are not written into your data model. So you cannot use this mechanism to provide parameters that you will subsequently update (such as the w/h parameters that the Flowchart Builder demo uses: if provided via the preconfigured parameters mechanism the UI would initially render correctly, but if the w/h values were updated, the template would not re-render. In the Flowchart Builder, the solution is to provide the w/h in the data model).

Switching off Preconfigured Parameters#

As mentioned above, there are some cases in which you cannot use preconfigured parameters. One such known case is when you are using AngularJS and you're taking advantage of the two-way data binding provided by its template engine. Preconfigured parameters (and function parameters, discussed below), cause a copy of the original data to be created, which then breaks AngularJS's two-way binding. A copy is created because the only other option is to copy the preconfigured parameters into the original data, which is almost certainly not what you want, given that they are a view concern.

To switch off preconfigured parameters and function parameters, set the enhancedView flag on your render call to false:

_toolkit.render({  container:"someElement",  view:{    nodes:{      "circleNodeDef" : {        templateId:"tmplCircle",        parameters:{          lineWidth:5,          radius:10,          fill:"black"        }      }    }  },  enhancedView:false});

Function Parameters#

Note Function parameters are known to break 2-way data binding when using the Toolkit with AngularJS, because the data object passed to the template is a copy of the original. To switch off function parameters, set enhancedView:false in your render call.

By default you can provide values in definitions as functions. These will be given the backing data for the object being rendered. An example definition:

_toolkit.render(document.getElementById("someElement"), {  view:{    nodes:{      "circleNodeDef" : {        templateId:"tmplCircle",        parameters:{          lineWidth:5,          radius:10,          fill:"black",          stroke:function(data) { return data.error ? "red" : "green"; }        }      }    }  }});

If we were to make this call:

var node = _toolkit.addNode({  type:"circleNodeDef",  error:true});

we'd end up with a circle that has a red outline. Otherwise we'd get a green outline.

Switching off Function Parameters#

See Switching off Preconfigured Parameters.


View Options#

The specific interface for a view now is SurfaceViewOptions

Home > @jsplumbtoolkit/browser-ui > SurfaceViewOptions

SurfaceViewOptions interface#

Signature:

export interface SurfaceViewOptions 

Properties#

PropertyTypeDescription
edges?ViewOptionsEntry<ViewEdgeOptions>(Optional)
groups?ViewOptionsEntry<ViewGroupOptions>(Optional)
nodes?ViewOptionsEntry<ViewNodeOptions>(Optional)
ports?ViewOptionsEntry<ViewPortOptions>(Optional)
states?Dictionary<any>(Optional)