- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
‎09-26-2024 10:07 AM - edited ‎07-30-2025 07:00 AM
Kanban Board Component - Part 1
Table of Contents
Introduction
This series of articles will walk you through an example configuration of the Kanban component. They will provide you with hard coded example configurations, explanations of properties, and a general overview of how to set up the component. It's important to keep in mind that the Kanban Board Component is highly customizable and uses other components for its configuration.
In this article we will cover prerequisites, go over the base configuration of the component, and showcase how to configure two example features (drag and drop & swimlanes). In the next article, linked in the top Article Directory and in the bottom of this article, we will provide some configuration information and examples for cards, lanes, and swimlanes.
This article assumes that you have some experience with UI Builder, we suggest that you first leverage one of these resources to learn more about configuring workspaces and experiences in UI Builder:
Prerequisites
The Kanban Board component comes pre-installed in your instance, but for the purposes of this tutorial you need to go to the Application Manager and install the sn-vtb
component library. Make sure that when you install or update this library, you click the "Load demo data" checkbox. This step is crucial and required for proper configuration of the Kanban Board component as part of this tutorial, as it installs the declarative actions we will be using.
Initial Configuration
- Go to UI Builder and add the Kanban Board component to a page. You can create a new page or utilize an already existing page in any workspace or experience.
- Create two Client State Parameters:
- Name:
cards
- Type: JSON
- Initial value:
[]
- Name:
lanes
- Type: JSON
- Initial value:
[]
- Name:
- Configure the Kanban Board component by setting the following component properties as follows:
- Create a new "GlideRecord Collection Query" Data Resource and configure as follows:
- Table: Incident
- Return fields: Number, Priority, Description, State, Short Description
- Order by: Updated
- Sort type: desc
- Max results: 20
- Create a new Client Script and name it "Format data". Copy the following code in to the script field:
function handler({api, event, helpers, imports}) { // Grabbing incidents from the GlideRecord Collection Query const incidents = api.data.gliderecord_collection_query_1.output.data.GlideRecord_Query.incident._results; const cards = []; const laneObject = {}; // For each incident, create a card and push to the array incidents.forEach((incident, index) => { cards.push({ id: incident._row_data.uniqueValue, title: incident.number.displayValue, order: index, // Sets what lane the card is assigned to lane_id: incident.priority.value, // Options, Attachments and Record properties are the bare minimum required to get the card template to render options: { "board_type": "FREEFORM", "card_type": "classic", }, attachments: [], record: { short_description: { display_value: incident.short_description.displayValue }, description: { display_value: incident.description.displayValue }, } }); // Starts setting up the lanes laneObject[incident.priority.value] = { value: incident.priority.value, displayValue: incident.priority.displayValue }; }); const lanes = []; Object.keys(laneObject).forEach(key => { // Creates the lanes lanes.push({ id: key, title: laneObject[key].displayValue, name: laneObject[key].displayValue, options: { "board_type": "FREEFORM" }, // Styles for the lane and lane header, CSS "_style_" : { "sn-lane": { width: "300px", padding: "5px", }, "sn-lane-header": { width: "300px", padding: "5px", }, } }); }); // Sets the client state parameters api.setState('lanes', lanes); api.setState('cards', cards); }​
- Navigate back to the GlideRecord Collection Query Data Resource and click on the Events tab.
- Add the event mapping "Data Fetch Succeeded" and then add an event handler to it that will execute the Format data client script.
- Save and test; you should see the Kanban Board render with your fetched data. Currently the cards will do nothing when clicked and they can't be dragged or dropped in any other lane. We will configure this in the remainder of the steps. If you don't see any cards, or only see the default data, try refreshing your browser after clearing the cache and try again.
Configuring Events
We will add an event for when the card gets clicked. When the sn-vtb-card
component is clicked, it generates the event VTB#CONFIRMATION_MODAL_SELECTED
. It has a payload consisting of two fields:
modalID
- String - Defines which modal should be used.modalData
- JSON - Returns the record data of the card.
To add the event:
- Create a new Client script and title it "Card is clicked", copy the following code into it:
function handler({api, event, helpers, imports}) { console.log("Card clicked event", event); } ​
- Click on the Body section of the page and, in the Page configuration panel to the right, click on Events.
- Go to the bottom to the "Handled events" section and click the
+ Add
button. - Add the following information to the "Create an event" modal:
- Event label:
VTB#CONFIRMATION_MODAL_SELECTED
- Click the
+Add
button next to the Payload fields heading and add two fields:- Name:
modalID
- Label: Modal ID
- Type: String
- Name:
modalData
- Label: Modal Data
- Type: JSON
- Name:
- Your modal should look like this when you're done:
- Event label:
- Navigate back to the top of the Events tab in the Page configuration panel, and click "Add event mapping".
- Select the event mapping we just created.
- When prompted to select an event handler, select the Client Script we just created titled "Card is clicked".
- Now that we have the event on the page, we need to add it to the Kanban Board. Click on the Kanban Board component in the component tree to the left.
- Scroll to the bottom of the component properties, and enter the following JSON in to the "Declarative-action map" field. This will pull in the declarative action we loaded through the demo data in the Prerequisites section of this article.
"VTB#CONFIRMATION_MODAL_SELECTED": { "actionDispatch": "VTB#CONFIRMATION_MODAL_SELECTED", "actionPayload": "{\r\n \"modalId\": \"{{modalId}}\",\r\n \"modalData\": \"{{modalData}}\"\r\n}", "actionType": "uxf_client_action", "assignmentId": "0c8e127453922010c5e2ddeeff7b125f", "label": "Confirmation Modal Selected", "name": "VTB#CONFIRMATION_MODAL_SELECTED" } }​
- Now that the declarative action has been mapped to the Kanban Board component, click on the "Configure declarative action event mappings" button at the very bottom of the component's configuration panel.
- Under "Confirmation Modal Selected", click "Add a new event handler".
- Fill in the fields as follow:
- Name: Card Clicked Mapping
- Dynamically data bind the following fields by clicking on the Data binding icon, double clicking on the empty space at the top where it says "Add a data output to this area", then typing in the information below:
- Modal ID:
@payload.modalID
- Modal Data:
@payload.modalData
- Modal ID:
- This is what it will look like when you have successfully double clicked, typed in the information, then clicked out of the box:
- Click "Save" once you've filled in both fields, then click "Done".
- Save the page and test. Open the browser console (F12) and observe the event firing the logging statement in the Client Script when you click on a card. You can use this payload information to configure a modal to be fired with the information from the record you clicked.
[INSTANCENAME]
part with your instance name.- URL:
https://[INSTANCENAME].service-now.com/now/nav/ui/classic/params/target/sys_declarative_action_assignment_list.do%3Fsysparm_query%3Dmodel%253D45757de50fa52010ad4437a98b767e33%26sysparm_first_row%3D1%26sysparm_view%3D
"VTB#CARD_MOVED"
"VTB#ADD_CARD_CLICKED"
"VTB#FREEFORM_CARD_ADDED"
"VTB#DATA_DRIVEN_CARD_ADDED"
"VTB#FREEFORM_PLACEHOLDER_CARD_REMOVED"
"VTB#CARD_ARCHIVED"
"VTB#LANE_MOVED"
"VTB#LANE_DELETED"
"VTB#CREATE_LANE_ACTION_PERFORMED"
"VTB#LANE_HIDE_SELECTED"
"VTB#LANE_HEADER_UPDATED"
"VTB#SWIMLANE_MOVED"
"VTB#SWIMLANE_ACTION_PERFORMED"
"VTB#AMB_MESSAGE_RECEIVED"
"VTB#CONFIRMATION_MODAL_SELECTED"
Drag and Drop
- On the Kanban Board component properties panel, set the following properties as follows:
- Vertical lane drag-and-drop option:
true
- Vertical lane-drag source display template:
sn-vtb-lane-src-placeholder
- Vertical lane-drop destination display template:
sn-vtb-lane-dest-placeholder
- Vertical lane drag-and-drop option:
- On the Kanban Board component properties panel, set the following properties as follows:
- Card drag-and-drop option:
true
- Card-drag source display template:
sn-vtb-card-src-placeholder
- Card-drop destination display template:
sn-vtb-card-dest-placeholder
- Card drag-and-drop option:
- Create a new Client Script, title it "Lane Moved".
- Add the following code to swap the position of the lane object in the lanes array:
function handler({api, event, helpers, imports}) { const {state: { lanes } } = api; const { payload : {fromPosition, toPosition} } = event; const temp = lanes[fromPosition]; const tempLanes = lanes.toSpliced(fromPosition, 1); const newLanes = tempLanes.toSpliced(toPosition, 0, temp); api.setState('lanes', newLanes); }
- Create a new Client Script, title it "Card Moved".
- Add the following code to change the lane ID of the card, and then update the order of all the cards in the lane to match the new order:
NOTE: If changing the lane updates a value for the record, you will need to include some type of script to validate the change and update the record on the backend. This Client Script does not update the record.function handler({api, event, helpers, imports}) { const {state: { cards } } = api; const { payload: { id, toLane, fromPosition, toPosition } } = event; const newCards = JSON.parse(JSON.stringify(cards)); const movedCard = newCards.find((card) => card.id === id); if (movedCard) { movedCard.lane_id = toLane; const cardsInLane = newCards .filter(card => card.lane_id === toLane) .sort((a,b) => a.order - b.order); movedCard.order = toPosition; cardsInLane.forEach((card, idx) => { if (card.id !== movedCard.id) { if (idx < toPosition) card.order = idx; else if (idx > toPosition) card.order = idx + 1; else card.order = toPosition - 1 >= 0 ? toPosition - 1 : toPosition + 1; } }); api.setState('cards', newCards); } }
- Navigate to the Kanban Board's events and add the "Now Visual Board - Card Moved" event mapping.
- Add the Client Script "Card Moved" to the event handler when prompted.
- Add the "Now Visual Board - Lane Moved" event mapping.
- Add the Client Script "Lane Moved" to the event handler when prompted.
- Save and test. Your lane and card changes should now reflect visually on the board. If you refresh, it will revert back to the data we set in the configuration above, as we have not created any scripts to edit the record data.
Swimlanes
- Create a new Client State Parameter:
- Name:
swimlanes
- Type: JSON
- Name:
- Go to the Kanban Board configuration panel, and set the following properties:
- Swimlane header template:
sn-vtb-swimlane-header
- Swimlanes: Dynamically data bind to the
swimlanes
Client State Parameter. (@STate.swimlanes
)
- Swimlane header template:
- In the Kanban Board configuration panel, go to the Styles tab.
- Set the Height to
1200 px
. - Update the "Format data" Client script by copy and pasting the following code:
function handler({api, event, helpers, imports}) { // Grabbing incidents from the GlideRecord Collection Query const incidents = api.data.gliderecord_collection_query_1.output.data.GlideRecord_Query.incident._results; const cards = []; const laneObject = {}; const swimLaneObject = {}; // For each incident, create a card and push to the array incidents.forEach((incident, index) => { cards.push({ id: incident._row_data.uniqueValue, title: incident.number.displayValue, order: index, // Sets what lane the card is assigned to lane_id: incident.priority.value, // Adding the swimlane ID and setting it to the incident's state swim_lane_id: incident.state.value, // Options, Attachments and Record properties are the bare minimum required to get the card template to render options: { "board_type": "FREEFORM", "card_type": "classic", }, attachments: [], record: { short_description: { display_value: incident.short_description.displayValue }, description: { display_value: incident.description.displayValue }, } }); // Starts setting up the lanes laneObject[incident.priority.value] = { value: incident.priority.value, displayValue: incident.priority.displayValue }; // Adding the swimlane object configuration swimLaneObject[incident.state.value] = { value: incident.state.value, displayValue: incident.state.displayValue }; }); const lanes = []; Object.keys(laneObject).forEach(key => { // Creates the lanes lanes.push({ id: key, title: laneObject[key].displayValue, name: laneObject[key].displayValue, options: { "board_type": "FREEFORM" }, // Styles for the lane and lane header, CSS "_style_" : { "sn-lane": { width: "300px", padding: "5px", }, "sn-lane-header": { width: "300px", padding: "5px", }, } }); }); // Configuring the swimlanes object const swimlanes = []; Object.keys(swimLaneObject).forEach((key, index) => { // Creates the swimlanes swimlanes.push({ id: key, order: index, cardsCount: cards.filter( (c) => c.swim_lane_id === key ).length, title: swimLaneObject[key].displayValue, name: swimLaneObject[key].displayValue, addLaneButton: false, options: { "board_type": "FREEFORM" }, "is_swim_lane": true, // Styles for the swimlane and the swimlane body, CSS "_style_": { 'sn-swimlane': { }, 'swimlane-body': { padding: '3px 3px', } }, }); }); // Sets the client state parameters api.setState('lanes', lanes); api.setState('cards', cards); // Adding the swimlanes client state parameter api.setState('swimlanes', swimlanes); }
- Save and test. You should now be able to see the swimlanes configured according to the state of the incident. Note you cannot collapse the swimlanes, and that the swimlane drag and drop has yet to be configured.
Enabling swimlane drag and drop
VTB#SWIMLANE_ACTION_PERFORMED
. It has a payload consisting of two fields:payload
- JSON - Action and payload data from the action generated by the swimlane.type
- String - Action type (ex.VTB#TOGGLE_SWIMLANE_BODY
)
- Create a new Client script and title it "Swimlane action", copy the following code into it:
function handler({api, event, helpers, imports}) { console.log("Swimlane event", event); }
- Click on the Body section of the page and, in the Page configuration panel to the right, click on Events.
- Go to the bottom to the "Handled events" section and click the
Add
button. - Add the following information to the "Create an event" modal:
- Event label:
VTB#SWIMLANE_ACTION_PERFORMED
- Click the "+Add" button next to the Payload fields heading and add two fields:
- Name:
payload
- Label: Payload
- Type: JSON
- Name:
type
- Label: Type
- Type: String
- Label: Type
- Name:
- Event label:
- Navigate back to the top of the Events tab in the Page configuration panel and click "Add event mapping".
- Select the event mapping we just created.
- When prompted to select an event handler, select the Client Script we just created titled "Swimlane action".
- Now that we have the event on the page, we need to add it to the Kanban Board. Click on the Kanban Board component in the component tree to the left.
- Scroll to the bottom of the component properties and enter the following JSON in to the "Declarative-action map" field, completely replacing what was already there.
{ "VTB#CONFIRMATION_MODAL_SELECTED": { "actionDispatch": "VTB#CONFIRMATION_MODAL_SELECTED", "actionPayload": "{\r\n \"modalId\": \"{{modalId}}\",\r\n \"modalData\": \"{{modalData}}\"\r\n}", "actionType": "uxf_client_action", "assignmentId": "0c8e127453922010c5e2ddeeff7b125f", "label": "Confirmation Modal Selected", "name": "VTB#CONFIRMATION_MODAL_SELECTED" }, "VTB#SWIMLANE_ACTION_PERFORMED": { "actionDispatch": "VTB#SWIMLANE_ACTION_PERFORMED", "actionPayload": "{\r\n \"payload\": \"{{payload}}\",\r\n \"type\": \"{{type}}\"\r\n}", "actionType": "uxf_client_action", "assignmentId": "f518127053922010c5e2ddeeff7b125a", "label": "Swimlane Action Performed", "name": "VTB#SWIMLANE_ACTION_PERFORMED" } }
- Now that the declarative action has been mapped to the Kanban Board component, click on the "Configure declarative action event mappings" button at the very bottom of the component's configuration panel.
- Under "Swimlane Action Performed", click "Add a new event handler".
- Fill in the fields as follow:
- Name: Swimlane Action Mapping
- Click the dropdown where you see the name of the event we created earlier, and select the
VTB#SWIMLANE_ACTION_PERFORMED
event instead. - Dynamically data bind the following fields by clicking on the Data binding icon, double clicking on the empty space at the top where it says "Add a data output to this area", then typing in the information below:
- Payload:
@payload.payload
- Type:
@payload.type
- Payload:
- Click "Save" once you've filled in both fields, then click "Done".
- Save the page and test by trying to toggle a swimlane closed. Open the browser console (F12) and observe the event firing the logging statement in the Client Script.
Conclusion
This article walked you through the initial configuration of the Kanban Board component. Feel free to post any questions you may have in the comments below.
Check out Part 2 of this article series for more information and reference material for further configuring these components.
You can also check out the You and I Builder Live! livestream where we covered this component.
Check out the Next Experience Center of Excellence for more resources
- 4,694 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hello!
The link to "Part 2 of this series " also links to the YouTube video and not another article.
Amazing job on this! Thank you SO MUCH!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Part 2 of this article has been published: https://www.servicenow.com/community/next-experience-articles/kanban-board-component-part-2-configur...
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
HI @MGOPW / @Brad Tilton
How to enable Quick Panel (User Names, Labels) on Top in Kanban Board. I don't find the details in document as well.
Regards,
Karthikeyan R
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
But did anyone have the same problem during development? My modalID and modalData are coming in null.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
I'm having the same problem. What I've found is that the Save in Step 12 of Configuring events is not actually saving the values. If you look the record up in the table "UX Add-on Event Mapping" you will see this:
As you can see, nothing saved. I've manually updated the record so it looks like this:
{
"container": {
"modalData": {
"binding": {
"address": [
"modalData"
]
},
"type": "EVENT_PAYLOAD_BINDING"
},
"modalId": {
"binding": {
"address": [
"modalId"
]
},
"type": "EVENT_PAYLOAD_BINDING"
}
},
"type": "MAP_CONTAINER"
}
And that seems to have done the trick..
@MGOPW is this something you need to know about?