CustomRenderer: canRender not called

Im trying to follow the GitHub - bpmn-io/bpmn-js-example-custom-rendering: An example of creating custom rendering for bpmn-js example.
for some reason the canRender(element) method is not called…

this is the code Im using for the customRender:

import BaseRenderer, { getRoundRectPath, is, isAny } from 'bpmn/dist/bpmn-modeler.development';

import {
  append as svgAppend,
  attr as svgAttr,
  create as svgCreate,
  remove as svgRemove
} from '../node_modules/tiny-svg/dist/index.cjs';


// Priority is set higher as 1000(=default) to make our rendercall first 
const HIGH_PRIORITY = 1500;
const TASK_BORDER_RADIUS = 2;

// Create custom renderer
// By extending BaseRenderer we make sure our renderer will be called whenever a shape or connection is to be rendered.
export default class CustomRenderer extends BaseRenderer {
  constructor(eventBus, bpmnRenderer) {
    super(eventBus, HIGH_PRIORITY);
    console.log("customrenderer available");

    this.bpmnRenderer = bpmnRenderer;
  }

  canRender(element) {
    console.log("canRender available in customrenderer");
    // only render tasks and events (ignore labels)
    return isAny(element, [ 'bpmn:Task', 'bpmn:Event' ]) && !element.labelTarget;
  }

  drawShape(parentNode, element) {
    const shape = this.bpmnRenderer.drawShape(parentNode, element);

    if (is(element, 'bpmn:Task')) {
      const rect = drawRect(parentNode, 100, 80, TASK_BORDER_RADIUS, '#52B415');

      prependTo(rect, parentNode);

      svgRemove(shape);

      return shape;
    }

    const rect = drawRect(parentNode, 30, 20, TASK_BORDER_RADIUS, '#cc0000');

    svgAttr(rect, {
      transform: 'translate(-20, -10)'
    });

    return shape;
  }

  getShapePath(shape) {
    if (is(shape, 'bpmn:Task')) {
      return getRoundRectPath(shape, TASK_BORDER_RADIUS);
    }

    return this.bpmnRenderer.getShapePath(shape);
  }
}

CustomRenderer.$inject = [ 'eventBus', 'bpmnRenderer' ];


// Helpers

// copied from https://github.com/bpmn-io/bpmn-js/blob/master/lib/draw/BpmnRenderer.js
function drawRect(parentNode, width, height, borderRadius, strokeColor) {
  const rect = svgCreate('rect');

  svgAttr(rect, {
    width: width,
    height: height,
    rx: borderRadius,
    ry: borderRadius,
    stroke: strokeColor || '#000',
    strokeWidth: 2,
    fill: '#fff'
  });

  svgAppend(parentNode, rect);

  return rect;
}

// copied from https://github.com/bpmn-io/diagram-js/blob/master/lib/core/GraphicsFactory.js
function prependTo(newNode, parentNode, siblingNode) {
  parentNode.insertBefore(newNode, siblingNode || parentNode.firstChild);
}

Did I forget something to add?

How are you including your custom renderer?

I’ve made an index.js with the following code:

import CustomRenderer from './CustomRenderer';

export default {
  __init__: [ 'customRenderer' ],
  customRenderer: [ 'type', CustomRenderer ]
};

and added the module to the modeler, the CustomRender class is being instantiated.

  additionalModules: [
    customRendererModule,

It’s difficult to find out why your custom renderer doesn’t work without knowing the entire setup. What changes did you make to the renderer from the example?

I understand it and really appreciate your help. I’m amazed how fast the support is here. I’m trying to give you more details of my setup so you might find the issue.

To begin with I also implemented the CustomPalette and CustomContexPad (from: GitHub - bpmn-io/bpmn-js-example-custom-controls: An example how to add custom editor controls to bpmn-js) on the exact same way as the CustomRenderer. With “the exact same way” I mean I created the a new directory with all required files:
image
All code is the same as in the example project except the import statements. The CustomPalette and CustomContexPad are both working correctly and all classes are being instantiated. So the index.js, CustomContexPad and CustomPalette file is not the issue.

The CustomRenderer.js file can be found in the post above.

Index.js:

import CustomContextPad from './CustomContextPad';
import CustomPalette from './CustomPalette';
import CustomRenderer from './CustomRenderer';

export default {
  __init__: [ 'customContextPad', 'customPalette', 'customRenderer'],
  customContextPad: [ 'type', CustomContextPad ],
  customPalette: [ 'type', CustomPalette ],
  customRenderer: [ 'type', CustomRenderer ]
};

furthermore the app.js file:

{ ... other imports ...}
import customModule from './bpmn_custom';

var container = $('#js-drop-zone');

var modeler = new Modeler({
  container: '#js-canvas',
  additionalModules: [
    customModule
  ]
});

async function openDiagram(xml) {

  try {

    await modeler.importXML(xml);
etc....

I dont think the html file could be the issue. again the only differents are the import statements, i’m using webpack with this conf:

var path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
    entry: './static/paper_dashboard/js/app.js',
    mode: 'development',
    devtool: 'source-map',
    experiments: {
      topLevelAwait: true
    },
    output: {
        path: path.resolve(__dirname, 'static/paper_dashboard/js/dist'),
        filename: 'bundled.js' 
    },
    module: {
      rules: [
        {
          test: /\.bpmn$/,
          use: {
            loader: 'raw-loader'
          }
        },
        {
          test: /\.js$/,
          exclude: /(node_modules)/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                ['@babel/preset-env', {targets: "defaults"}]
              ]
            }
          }
        },
        {
          test: /\.css$/,
          use: [MiniCssExtractPlugin.loader, 'css-loader'],
        },
        {
          test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
          use: [
            {
              loader: 'file-loader',
              options: {
                name: '[name].[ext]',
                outputPath: 'fonts/'
              }
            }
          ]
        }
      ]
    },
    plugins: [
      new MiniCssExtractPlugin({
        filename: 'main.css',
      }),
    ],
};

Result:
image

I expected the diagram to load including the customrenderer config options just like in your example project

Not sure why it’s not working. The only difference I can spot is the way you import it:

import BaseRenderer, { getRoundRectPath, is, isAny } from 'bpmn/dist/bpmn-modeler.development';

Can you debug the issue by inspecting the event bus to see if your custom renderer has actually subscribed to render.shape?

Alternatively, you could share your code as a CodeSandbox.

image
you mean like this? Im a “new” to programming and trying to understand/learn a lot of things. doesn’t look like it has actually subscribed?
PS, rightclick the image and open in a new tab, otherwise you will not see the right part of the picture…

I think I found the issue it’ s indeed the way of how I am importing things. I’m so sorry for wasting your time :sweat: