preface
Q: What is bPMn.js? 🤔 ️
“
Bpmn. js is a BPMN2.0 rendering toolkit and Web modeler that allows you to draw flowcharts in the front end.
Q: Why did I write this series? 🤔 ️
“
Due to the business needs of the company, bPMN. js is used in the project. However, as the developer of BPMN. js is a foreign friend, there are few textbooks and no detailed documents on this aspect in China. So a lot of ways to use a lot of pits have to find their own. After mulling it over, I decided to write a series of textbooks on it to help more bPMn.js users or developers who want to find a good way to draw flowcharts. It is also a kind of consolidation of their own.
Because it is a series of articles, so the update may be more frequent, if you accidentally brush and not what you need also please understand 😊.
Github address of all textbooks: bPMN-Chinese-Document
Properties- Panel
In the previous chapter, I mainly introduced how to expand on the basis of the original property-panel, but many friends will say that I dislike the original property bar style 😅… I’m a mature front end, I need to have my own ideas…
OK… I respect you… In this chapter, Lin Zaidai will teach you how to beautify our properties-panel😊.
By reading this chapter you can learn:
- Modify the default style of the property bar
- The custom
properties-panel
- Changing a Node Name
label
attribute - Changing the Node color
color
attribute - Modify the
event
The node type - Modify the
Task
Types of nodes - Initialize the
properties-panel
And set some default values
Modify the default style of the property bar
Let’s take a look at what we can achieve by changing the default style of the property bar 🤔️!
As shown at 👆, you can customize the property bar with different theme colors to make it look better.
To change the style of the default property bar, simply open the console (Window: F12, Mac: Option + Command + I), examine the element, find the class of each element, and override its original property in the code.
Remember when we referenced the property-panel style in the project’s main.js?
// main.js
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // Right toolbar style
Copy the code
Now let’s create a styles folder in the project along with a bpmn-properties-theme-red. CSS file that will be used to write the property bar styles we need to customize.
Then reference it in main.js, preferably after the original style:
// main.js
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // Right toolbar style
import './styles/bpmn-properties-theme-red.css' // Red theme
Copy the code
For example, now I want to change the font color at the head of the property bar:
Find this class by reviewing the elements and then modify it in bPMn-properties-theme-red.css:
.bpp-properties-header>.label {
color: rgb(239.112.96);
font-size: 16px;
}
Copy the code
Save and open the page again to see the effect.
Of course, I’m just showing you how to change the default styles, so I’m just using the simplest CSS. There’s a lot of room to expand, you can write in less or sass, you can do your own theme switching, etc. Hope to inspire you 😊…
If you want to steal will be lazy… Take Lin’s dull style directly…
Github address of 👆 case above:
The LinDaiDai/BPMN – vue – properties – a panel
The customproperties-panel
Sometimes you might want to customize a property bar instead of using the official property-Panel, which is also possible.
For example, I want to display different attribute configurations on the right side for different node types, and then update them synchronously to XML after editing.
The implementation principle is also mentioned in the previous bPMN.js tutorial – Properties, mainly using the updateProperties() method to modify the attributes on the element node.
Now let’s see how to encapsulate such a custom property bar 😊.
preparation
Since custom property bars can be a lot of code and can involve complex business components, I recommend that you take them out of the way you introduced them in BPMn.js and encapsulate them into a generic custom property bar component.
The component’sprops
Now that we’ve decided to pull it out as a component, what should the props of this component be set to?
(Props is the value that the parent component passes to the child component, in this case the parent element is where bPMN.js is introduced, and the child element is the custom property bar component.)
To clarify our requirements, we need to click on different elements to render different configurations, so we can pass in a single element as props.
However, later in the process of writing, I found that there are a lot of binding events are related to Modeler. If these binding events are completed in the parent component, it is against our will to remove the separate component 🤔️?
So here, I’m writing the entire Modeler as props. This makes it easier to bind events to modeler or element.
OK… With props in mind, let’s create a custom-properties-Panel folder in the Components folder and create a file called propertiesView.vue in it to write our custom property bar component.
We expect this component to be able to be used in HTML like this:
<div class="containers" ref="content">
<div class="canvas" ref="canvas"></div>
<properties-view v-if="bpmnModeler" :modeler="bpmnModeler"></properties-view>
</div>
Copy the code
(bpmnModeler is the Modeler object you create using New bpmnModeler)
Write custom property bar components
1. Component structure
Let’s start by building the infrastructure for this component:
<! --PropertiesView.vue-->
<template>
<div class="custom-properties-panel"></div>
</template>
<script>
export default {
name: 'PropertiesView'.props: {
modeler: {
type: Object.default: (a)= > ({})
}
},
data () {
return {
selectedElements: [].// The set of elements currently selected
element: null // The element currently clicked
}
},
created () {
this.init()
},
methods: {
init () {}
}
}
</script>
<style scoped></style>
Copy the code
2. The componenthtml
code
Let me add something to this component first:
<template>
<div class="custom-properties-panel">
<div class="empty" v-if="selectedElements.length<=0">Select an element</div>
<div class="empty" v-else-if="selectedElements.length>1">Only one element can be selected</div>
<div v-else>
<fieldset class="element-item">
<label>id</label>
<span>{{ element.id }}</span>
</fieldset>
<fieldset class="element-item">
<label>name</label>
<input :value="element.name" @change="(event) => changeField(event, 'name')" />
</fieldset>
<fieldset class="element-item">
<label>customProps</label>
<input :value="element.name" @change="(event) => changeField(event, 'customProps')" />
</fieldset>
</div>
</div>
</template>
Copy the code
As 👆, I added three properties, ID, name, customProps. At the same time, there is a selectedElements judgment.
This is because when we’re working with graphics, if you use command + left, It is possible to select multiple nodes, so you need to make a decision.
3. Componentsjs
code
If you’ve read a lot of code written by Lin Nerdy, you’ll notice that I prefer to refer some of my initialization code to a function called init(), which is a personal coding habit.
Here, our initialization function does the following:
- use
selection.changed
Listen for selected elements; - use
element.changed
Listen for changed elements.
init () {
const { modeler } = this // The modeler passed in by the parent component
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection // Array, possibly multiple
this.element = e.newSelection[0] // Take the first one by default
})
modeler.on('element.changed', e => {
const { element } = e
const { element: currentElement } = this
if(! currentElement) {return
}
// update panel, if currently selected element changed
if (element.id === currentElement.id) {
this.element = element
}
})
}
Copy the code
Alternatively, we can write a common attribute update method to update an attribute on an element:
/** * Updates the element attribute *@param { Object } Attributes to update, such as {name: ", id: "} */
updateProperties(properties) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateProperties(element, properties)
}
Copy the code
Then add an @change event to the input or other control on the property bar to update element synchronously when the content in the control changes.
/** * Changes the event triggered by the control *@param { Object } The input Event *@param { String } The name of the property to be modified */
changeField (event, type) {
const value = event.target.value
let properties = {}
properties[type] = value
this.element[type] = value
this.updateProperties(properties) // Call the property update method
}
Copy the code
4. Complete component code
Combine all the code from 👆 above:
<template>
<div class="custom-properties-panel">
<div class="empty" v-if="selectedElements.length<=0">Select an element</div>
<div class="empty" v-else-if="selectedElements.length>1">Only one element can be selected</div>
<div v-else>
<fieldset class="element-item">
<label>id</label>
<span>{{ element.id }}</span>
</fieldset>
<fieldset class="element-item">
<label>name</label>
<input :value="element.name" @change="(event) => changeField(event, 'name')" />
</fieldset>
<fieldset class="element-item">
<label>customProps</label>
<input :value="element.name" @change="(event) => changeField(event, 'customProps')" />
</fieldset>
</div>
</div>
</template>
<script>
export default {
name: 'PropertiesView'.props: {
modeler: {
type: Object.default: (a)= > ({})
}
},
data() {
return {
selectedElements: [].element: null
}
},
created() {
this.init()
},
methods: {
init() {
const { modeler } = this
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection
this.element = e.newSelection[0]
})
modeler.on('element.changed', e => {
const { element } = e
const { element: currentElement } = this
if(! currentElement) {return
}
// update panel, if currently selected element changed
if (element.id === currentElement.id) {
this.element = element
}
})
},
/** * Changes the event triggered by the control *@param { Object } The input Event *@param { String } The name of the property to be modified */
changeField(event, type) {
const value = event.target.value
let properties = {}
properties[type] = value
this.element[type] = value
this.updateProperties(properties)
},
updateName(name) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
// modeling.updateLabel(element, name)
modeling.updateProperties(element, {
name
})
},
/** * Updates the element attribute *@param { Object } Attributes to update, such as {name: "} */
updateProperties(properties) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateProperties(element, properties)
}
}
}
</script>
<style scoped>
/** More code is available on git, see the git link at the bottom
.custom-properties-panel {
position: absolute;
right: 0;
top: 0;
width: 300px;
background-color: #fff9f9;
border-color: rgba(0.0.0.0.09);
box-shadow: 0 2px 8px rgba(0.0.0.0.09);
padding: 20px;
}
</style>
Copy the code
Changing a Node Namelabel
attribute
In the example above, we showed how to modify the attribute of an element. If you want to modify the label of an element, one way is to modify the name attribute as above 👆, or to update it with the modeling. UpdateLabel method:
updateName(name) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateLabel(element, name)
/ / is equivalent to modeling. UpdateProperties (element, {name})
},
Copy the code
Changing the Node colorcolor
attribute
How do you get the user to manually change the color of the node?
You can use the modeling.setcolor method.
Let’s say I add a line of property to my code:
<fieldset class="element-item">
<label>The node color</label>
<input type="color" :value="element.color" @change="(event) => changeField(event, 'color')" />
</fieldset>
Copy the code
Then transform the following changeField methods:
/** * Changes the event triggered by the control *@param { Object } The input Event *@param { String } The name of the property to be modified */
changeField(event, type) {
const value = event.target.value
let properties = {}
properties[type] = value
if (type === 'color') { // If it is the color attribute
this.onChangeColor(value)
}
this.element[type] = value
this.updateProperties(properties)
},
onChangeColor(color) {
const { modeler, element } = this
const modeling = this.modeler.get('modeling')
modeling.setColor(element, {
fill: color,
stroke: null})},Copy the code
The setColor method accepts two attributes:
fill
: Filling color of the nodestroke
: The color and node of the node borderlabel
The color of the
Here I show you how to change the node’s fill color, which is called fill, and of course you can change the stroke, which looks like this:
Interestingly, if you set both fill and stroke to color:
modeling.setColor(element, {
fill: color,
stroke: color
})
Copy the code
Then you can’t see the label… This is because stroke also changes the color of the label so that it becomes the same as fill.
But you don’t usually set the border and the fill to the same color… There is no need to…
If you really want to solve this problem, here is a bad way to force the label style to change in the global CSS:
.djs-label {
fill: # 000! important;
}
Copy the code
Modify theevent
The node type
In some cases, we may also need to change the node type in the custom properties bar, such as on the Start node, by clicking the contextPad spanner:
Implement this function. We need to use bpmnReplace replaceElement this method.
First let’s look at where the event property is placed.
I changed the type of the start node to MessageEventDefinition
It is on the corresponding element. The businessObject. EventDefinitions in the array, and if the StartEvent and EndEvent, the array as undefinded.
Let’s see how this function is implemented 😄.
First add a dropdown to the HTML to change the event node type:
<! --PropertiesView.vue-->
<template>
<fieldset class="element-item" v-if="isEvent">
<label>Example Modify the Event node type</label>
<select @change="changeEventType" :value="eventType">
<option
v-for="option in eventTypes"
:key="option.value"
:value="option.value"
>{{ option.label }}</option>
</select>
</fieldset>
</template>
<script>
export default {
data () {
return {
eventTypes: [{label: 'default'.value: ' ' },
{ label: 'MessageEventDefinition'.value: 'bpmn:MessageEventDefinition' },
{ label: 'TimerEventDefinition'.value: 'bpmn:TimerEventDefinition' },
{ label: 'ConditionalEventDefinition'.value: 'bpmn:ConditionalEventDefinition'}].eventType: ' '}},methods: {
verifyIsEvent (type) { // Check whether the type is event
return type.includes('Event')
},
changeEventType (event) {}
},
computed: {
isEvent() { // Determine whether the element type currently clicked is event
const { element } = this
return this.verifyIsEvent(element.type)
}
}
}
</script>
Copy the code
Ok, complete the basic code above 👆, the main logic is to change the value of the drop-down box:
changeEventType(event) { // Change the drop-down box
const { modeler, element } = this
const value = event.target.value
const bpmnReplace = modeler.get('bpmnReplace')
this.eventType = value
bpmnReplace.replaceElement(element, {
type: element.businessObject.$type,
eventDefinitionType: value
})
},
Copy the code
Now if you change the drop-down box, you can change eventDefinitionType, but there’s a problem, if you click on another node and then click back to the start node, the default value of the drop-down box won’t be correct, That is, we also need to get the eventDefinitionType value for the start node itself.
At this point, we can do this kind of initializing properties-panel in the Selection. changed listener event.
init () {
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection
this.element = e.newSelection[0]
console.log(this.element)
this.setDefaultProperties() // Set some default values
})
}
setDefaultProperties() {
const { element } = this
if (element) {
const { type, businessObject } = element
if (this.verifyIsEvent(type)) { // If the type is event
// Get the default eventDefinitionType
this.eventType = businessObject.eventDefinitions ? businessObject.eventDefinitions[0] ['$type'] : ' '}}}Copy the code
Modify theTask
Types of nodes
We already know how to modify the event node. What about the Task node 🤔️?
It’s pretty much the same thing.
Also, let’s add a dropdown for Task attributes in the HTML:
<! --PropertiesView.vue-->
<template>
<fieldset class="element-item" v-if="isTask">
<label>Example Modify the Task node type</label>
<select @change="changeTaskType" :value="taskType">
<option
v-for="option in taskTypes"
:key="option.value"
:value="option.value"
>{{ option.label }}</option>
</select>
</fieldset>
</template>
<script>
export default {
data () {
return {
taskTypes: [{label: 'Task'.value: 'bpmn:Task' },
{ label: 'ServiceTask'.value: 'bpmn:ServiceTask' },
{ label: 'SendTask'.value: 'bpmn:SendTask' },
{ label: 'UserTask'.value: 'bpmn:UserTask'}].taskType: ' '}},methods: {
verifyIsTask(type) {
return type.includes('Task')
},
changeTaskType (event) {}
},
computed: {
isTask() { // Determine whether the element type being clicked is task
const { element } = this
return this.verifyIsTask(element.type)
}
}
}
</script>
Copy the code
Then when changing the Task dropdown:
changeTaskType(event) {
const { modeler, element } = this
const value = event.target.value // The value selected in the current drop-down box
const bpmnReplace = modeler.get('bpmnReplace')
bpmnReplace.replaceElement(element, {
type: value // Change type directly})}Copy the code
Initialize theproperties-panel
And set some default values
When we set our own custom property bar, we may need to make different business logic judgment according to different node types, and set some default values of properties-panel, such as 👆, change the event type, at this time we can do 🤔️?
As with changing the Event type, we can do this in the selection. Changed listening event.
init () {
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection
this.element = e.newSelection[0]
console.log(this.element)
this.setDefaultProperties() // Set some default values
})
}
setDefaultProperties() {
const { element } = this
if (element) {
// Here you can get all the properties of the currently clicked node
const { type, businessObject } = element
// doSomeThing}}Copy the code
In fact, it is the same as the initialization of modifying the event type introduced at 👆 above. However, I am afraid that some friends directly skipped the modification of the event type and did not see this part, so I will mention it separately.
For example, if we want to get the label from our Shape and synchronize it to the custom properties bar on the right, we can do this:
In setDefaultProperties we can get the element that we clicked on via this.element, and when we print out the element, we’ll see that label is actually the name property in businessObject, so we just need to do something:
element['name'] = businessObject.name
Copy the code
If you change the label on the graph or the name in the custom property bar, it will be synchronized. See the code in Github for details.
replace
The type of
Above 👆 we introduced how Event and Task elements are converted to types. This example only demonstrates a few types, so where to look at all types 🤔️?
You can find the bpmn.js source here:
https://github.com/bpmn-io/bpmn-js/blob/develop/lib/features/replace/ReplaceOptions.js
Copy the code
You can even export the desired content directly into the code:
import { START_EVENT } from 'bpmn-js/lib/features/replace/ReplaceOptions.js'
Copy the code
After the language
Above 👆 textbook case code address: LinDaiDai/ bPMn-vue-properties-panel
By the end of this chapter, properties-panel is a general introduction. Whether you want to use the original properties-Panel or use a custom properties-panel, I believe you have mastered 😄…
In the follow-up Lin Dai may be based on bPMn.js source code to list some commonly used properties and methods, so that you better understand bPMn.js.
Soon to the New Year 🧨…
Code finished this chapter, Lin Dull also began to pack home 😄…
Happy New Year again ~ 🔥 🎆
Finally, if you are also interested in bPMn. js, you can join our bPMn. js communication group 👇👇👇, learn together and make progress together.
Follow Lin Daodi’s public account and choose “bPMn. js group” in the “Other” menu at 😊.
For the full catalogue of the series, please check here: “The most detailed BPMN.js textbook in the whole Web”.
Series related recommendations:
“The most detailed BPMN.JS textbook in the Whole Web – Basic Chapter”
The most detailed BPMN.JS textbook in the whole Web – Events
The most detailed BPMN.js textbook on the Whole web – Renderer
The most detailed BPMN.js textbook -contextPad
“The most detailed BPMN. js textbook – Customize Palette”
“The most detailed BPMN. js textbook in the whole web – Editing and Deleting nodes”
“The most detailed BPMN.js textbook in the whole Web – Packaging Components”
The most detailed bPMN.js textbook in the whole web – Properties
“The most detailed bPMN. js textbook -properties-panel (1)”;