全網最詳bpmn.js教材-事件篇

LinDaiDai_霖呆呆發表於2019-12-10

前言

Q: bpmn.js是什麼? ?️

bpmn.js是一個BPMN2.0渲染工具包和web建模器, 使得畫流程圖的功能在前端來完成.

Q: 我為什麼要寫該系列的教材? ?️

因為公司業務的需要因而要在專案中使用到bpmn.js,但是由於bpmn.js的開發者是國外友人, 因此國內對這方面的教材很少, 也沒有詳細的文件. 所以很多使用方式很多坑都得自己去找.在將其琢磨完之後, 決定寫一系列關於它的教材來幫助更多bpmn.js的使用者或者是期於找到一種好的繪製流程圖的開發者. 同時也是自己對其的一種鞏固.

由於是系列的文章, 所以更新的可能會比較頻繁, 您要是無意間刷到了且不是您所需要的還請諒解?.

不求贊?不求心❤️. 只希望能對你有一點小小的幫助.

事件篇

上一章節我們介紹了利用bpmn.js與後臺進行互動, 要是對bpmn.js不瞭解的小夥請移步:

《全網最詳bpmn.js教材-http請求篇》

這一章節要講解是關於bpmn.js的一些事件, 通過學習此章節你可以學習到:

  • 監聽modeler並繫結事件
  • 監聽element並繫結事件
  • 通過監聽事件判斷操作方式

監聽modeler並繫結事件

很多時候你期望的是在使用者在進行不同操作的時候能夠監聽到他操作的是什麼, 從而做想要做的事情.

是進行了shape的新增還是進行了線的新增.

比如如下的一些監聽事件:

  • shape.added 新增一個shape之後觸發;
  • shape.move.end 移動完一個shape之後觸發;
  • shape.removed 刪除一個shape之後觸發;

繼續在專案案例bpmn-vue-basic的基礎上建立一個event.vue檔案:

並在success()函式中新增上監聽事件的函式:

// event.vue
<script>
...
success () {
  this.addModelerListener()
},
// 監聽 modeler
addModelerListener() {
  const bpmnjs = this.bpmnModeler
  const that = this
  // 這裡我是用了一個forEach給modeler上新增要繫結的事件
  const events = ['shape.added', 'shape.move.end', 'shape.removed', 'connect.end', 			'connect.move']
  events.forEach(function(event) {
    that.bpmnModeler.on(event, e => {
      console.log(event, e)
      var elementRegistry = bpmnjs.get('elementRegistry')
      var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
      console.log(shape)
    })
  })
},
複製程式碼

如圖所示, 在這裡你就可以獲取到相關節點的所有資訊了:

img1
img1

案例Git地址: LinDaiDai-bpmn.js案例event.vue

其實具體有哪些事件我在官網上都沒有找到說明, 以上只是我在查詢到bpmn.io/diagram.js/…檔案之後, 取的一些我專案裡有用到的事件.

監聽element並繫結事件

上面介紹的是監聽modeler並繫結事件, 可能你也需要監聽使用者點選圖形上的element或者監聽某個element改變:

  • element.click 點選元素;
  • element.changed 當元素髮生改變的時候(包括新增、移動、刪除元素)

繼續在success()上新增監聽事件:

// event.vue
<script>
...
success () {
	...
	this.addEventBusListener()
},
addEventBusListener () {
	let that = this
  const eventBus = this.bpmnModeler.get('eventBus') // 需要使用eventBus
  const eventTypes = ['element.click', 'element.changed'] // 需要監聽的事件集合
  eventTypes.forEach(function(eventType) {
    eventBus.on(eventType, function(e) {
      console.log(e)
    })
  })
}
</script>
複製程式碼

配置好addEventBusListener()函式後, 在進行元素的點選、新增、移動、刪除的時候都能監聽到了.

但是有一點很不好, 你在點選“畫布”的時候, 也就是根元素也可能會觸發此事件, 我們一般都不希望此時會觸發, 因此我們可以在on回撥中新增一些判斷, 來避免掉不需要的情況:

eventBus.on(eventType, function(e) {
  if (!e || e.element.type == 'bpmn:Process') return // 這裡我的根元素是bpmn:Process
  console.log(e)
})
複製程式碼

此時我們可以把監聽到返回的節點資訊列印出來看看:

img2
img2

如上圖, 它會列印出該節點的Shape資訊和DOM資訊等, 但我們可能只關注於Shape資訊(也就是該節點的id、type等等資訊), 此時我們可以使用elementRegistry來獲取Shape資訊:

eventBus.on(eventType, function(e) {
  if (!e || e.element.type == 'bpmn:Process') return // 這裡我的根元素是bpmn:Process
  console.log(e)
  var elementRegistry = this.bpmnModeler.get('elementRegistry')
  var shape = elementRegistry.get(e.element.id) // 傳遞id進去
  console.log(shape) // {Shape}
  console.log(e.element) // {Shape}
  console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
})

複製程式碼

或者你也可以直接就用e.element獲取到Shape的資訊, 我比較了一下它們兩是一樣的. 但是官方是推薦使用elementRegistry的方式.

通過監聽事件判斷操作方式

上面我們已經介紹了modelerelement的監聽繫結方式, 在事件應用中, 你更多的需要知道使用者要進行什麼操作, 好寫對應的業務邏輯.

這裡我就以我工作中要用到的場景為案例進行講解.

  • 新增了shape
  • 新增了線(connection)
  • 刪除了shape和connection
  • 移動了shape和線
// event.vue
    ...
    success () {
      this.addModelerListener()
      this.addEventBusListener()
    },
    // 新增繫結事件
    addBpmnListener () {
      const that = this
      // 獲取a標籤dom節點
      const downloadLink = this.$refs.saveDiagram
      const downloadSvgLink = this.$refs.saveSvg
        // 給圖繫結事件,當圖有發生改變就會觸發這個事件
      this.bpmnModeler.on('commandStack.changed', function () {
        that.saveSVG(function(err, svg) {
            that.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg)
        })
        that.saveDiagram(function(err, xml) {
            that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)
        })
      })
    },
    addModelerListener() {
      // 監聽 modeler
      const bpmnjs = this.bpmnModeler
      const that = this
      // 'shape.removed', 'connect.end', 'connect.move'
      const events = ['shape.added', 'shape.move.end', 'shape.removed']
      events.forEach(function(event) {
        that.bpmnModeler.on(event, e => {
          var elementRegistry = bpmnjs.get('elementRegistry')
          var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
          // console.log(shape)
          if (event === 'shape.added') {
            console.log('新增了shape')
          } else if (event === 'shape.move.end') {
            console.log('移動了shape')
          } else if (event === 'shape.removed') {
            console.log('刪除了shape')
          }
        })
      })
    },
    addEventBusListener() {
      // 監聽 element
      let that = this
      const eventBus = this.bpmnModeler.get('eventBus')
      const eventTypes = ['element.click', 'element.changed']
      eventTypes.forEach(function(eventType) {
        eventBus.on(eventType, function(e) {
          if (!e || e.element.type == 'bpmn:Process') return
          if (eventType === 'element.changed') {
            that.elementChanged(eventType, e)
          } else if (eventType === 'element.click') {
            console.log('點選了element')
          }
        })
      })
    },
    elementChanged(eventType, e) {
      var shape = this.getShape(e.element.id)
      if (!shape) {
        // 若是shape為null則表示刪除, 無論是shape還是connect刪除都呼叫此處
        console.log('無效的shape')
        // 由於上面已經用 shape.removed 檢測了shape的刪除, 因此這裡只判斷是否是線
        if (this.isSequenceFlow(shape.type)) {
          console.log('刪除了線')
        }
      }
      if (!this.isInvalid(shape.type)) {
        if (this.isSequenceFlow(shape.type)) {
          console.log('改變了線')
        }
      }
    },
    getShape(id) {
      var elementRegistry = this.bpmnModeler.get('elementRegistry')
      return elementRegistry.get(id)
    },
    isInvalid (param) { // 判斷是否是無效的值
      return param === null || param === undefined || param === ''
    },
    isSequenceFlow (type) { // 判斷是否是線
      return type === 'bpmn:SequenceFlow'
    }
複製程式碼

案例Git地址: LinDaiDai-bpmn.js案例event.vue 喜歡的小夥伴請給個Star?呀, 謝謝?

後語

系列全部目錄請檢視此處: 《全網最詳bpmn.js教材》

系列相關推薦:

《全網最詳bpmn.js教材-基礎篇》

《全網最詳bpmn.js教材-http請求篇》

《全網最詳bpmn.js教材-renderer篇》

《全網最詳bpmn.js教材-contextPad篇》

《全網最詳bpmn.js教材-編輯、刪除節點篇》

相關文章