Dmn-js - how to highlight selected rules?

Hi folks,

there are already great examples on how to embed and use dmn-js within custom applications. See https://github.com/bpmn-io/dmn-js-examples.

Similar to bpm-js, you can certainly access the individual components of the viewer to enhance the rendering according to your own needs.

Question: is there an easy way to access and highlight selected specific DMN rules / table rows in order to change the style, e.g. background, color, etc.?

Thanks in advance,
Gunnar

If it’s just CSS styles change, then I’m overriding the styles with my CSS. using developer tools, finding out the appropriate element styles and overriding the same. There could be elegant way, but for now this is how i’m overriding the look and feel of default styles

What I’m looking for is a way to do something similar to BPMN Models in dmn-js, e.g. with markers:

Sample from bpm-js:

.highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
  fill: green !important; /* color elements as green */
}

viewer.importXML(diagramXML, function() {
  var canvas = viewer.get('canvas');
  canvas.addMarker('UserTask_1', 'highlight');
});

Adding markers to individual selected rules this way would be cool. Of course there are workarounds by finding the corresponding elements within the DOM tree and adding css otherwise. It’s just that I’m looking for a more cleaner way.

You have to know that the decision tables component is not built on diagram-js, but table-js, which do not have a Canvas module, and also not something like that, as far as I can see. So you won’t find an addMarker API for now, unfortunately.

But we’re open for contributions adding such a functionality :rocket:

Thanks @Niklas_Kiefer,

so i stick to the use of standard html DOM element methods.

For those of you interested how to accomplish this in Vue.js using something similar to https://github.com/bpmn-io/vue-bpmn for DMN - here are some snippets:

1.) Usage

<template>
    ...
    <dmn-io
      :url="diagramXmlModelUrl"
      :highlightRuleNumbers="highlightRuleNumbers"
    />
    ...
</template>

<style>
  .dmn-highlight {
    background-color: #4caf507d !important
  }
</style>

<script>
  import VueDmn from '/../utils/vue-dmn-io'

  export default {
    components: {
      'dmn-io': VueDmn,
    },

    data () {
      return {
        diagramXmlModelUrl: null,
        highlightRuleNumbers: [],
       ...
      }
    },

   ...
   /* 
      Some code to 
      a) get the URL of your decision XML
      b) calculate the rule numbers to highlight (e.g. [2,4] )
   */
   ...

</script>

2.) Implementation of vue-dmn-io.vue

The important section for highlighting DMN table rows searches for the table body of the DMN table and then modifies the class of selected table rows. If someone of you has a better or more elegant solution - please let me know.

        if (self.highlightRuleNumbers) {
          var tbody = self.$refs.container.getElementsByTagName('tbody')[0]
          self.highlightRuleNumbers.forEach(ruleNo => tbody.childNodes[ruleNo - 1].setAttribute('class', 'dmn-highlight'))
        }

Full code - including migration of DMN 1.1:

<template>
  <div
    ref="container"
    class="vue-dmn-diagram-container"
  />
</template>

<script>
  import { migrateDiagram } from '@bpmn-io/dmn-migrate'
  import 'dmn-js/dist/assets/dmn-js-decision-table.css'
  import DmnJS from 'dmn-js/dist/dmn-viewer.production.min.js'

  export default {
    name: 'vue-dmn-io',
    props: {
      url: {
        type: String,
        required: true,
      },
      highlightRuleNumbers: {
        type: Array,
        required: false,
      },
    },
    data: function () {
      return {
        diagramXML: null,
      }
    },
    mounted: function () {
      var container = this.$refs.container
      var self = this
      this.dmnViewer = new DmnJS({
        container: container,
      })
      this.dmnViewer.on('import.done', function (event) {
        var error = event.error
        var warnings = event.warnings
        if (error) {
          self.$emit('error', error)
        } else {
          self.$emit('shown', warnings)
        }
        if (self.highlightRuleNumbers) {
          var tbody = self.$refs.container.getElementsByTagName('tbody')[0]
          self.highlightRuleNumbers.forEach(ruleNo => tbody.childNodes[ruleNo - 1].setAttribute('class', 'dmn-highlight'))
        }
      })
      if (this.url) {
        this.fetchDiagram(this.url)
      }
    },
    beforeDestroy: function () {
      this.dmnViewer.destroy()
    },
    watch: {
      url: function (val) {
        this.$emit('loading')
        this.fetchDiagram(this.url)
      },
      diagramXML: async function (xml) {
        xml = await migrateDiagram(xml)
        this.dmnViewer.importXML(xml)
      },
    },
    methods: {
      fetchDiagram: function (url) {
        var self = this
        fetch(url)
          .then(function (response) {
            return response.text()
          })
          .then(function (text) {
            self.diagramXML = text
          })
          .catch(function (err) {
            self.$emit('error', err)
          })
      },
    },
  }
</script>

<style>
  .vue-dmn-diagram-container {
    height: 100%;
    width: 100%;
  }
</style>

3.) DMN 1.1 POC / Sample Result

grafik

Perhaps this will help others with similar needs to kick start their own solutions. :rocket:

And many thanks to the authors of bpmn-io - this is really great stuff :slight_smile:
Gunnar

1 Like

Thanks for sharing your solution!