- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-09-2022 04:41 PM
Hello,
I'm trying to add a content tree component to the page. This component should display a tree generated based on the table. The table has a reference key to itself.
Is there any tutorial or example how to do this using a dynamic binding?
What kind of Data Resource can be used to retrieve data from a table and populate "Items" parameter of the Content Tree component?
Thank you.
Solved! Go to Solution.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-21-2022 09:17 AM
Hi Sergey,
I have used the content tree component already. I'm using the Data Broker Server Script (in navigator called: "Transforms". Don't be confused! ;))
You must prepare an array of object(var answer in my script) with the properties "id" and "label".
For additional hints see my article: UI Builder Component: Content Tree
Here an example to work with different tables in one tree.
the script (it's working with 4 different tables, yours should be a more simple one):
function transform(input) {
var piID = input.productID;
var answer = [];
var expanded = [];
var level = 0;
var MAXLEVEL = 9; // maximum for recursion level
var inventoryObj = getInventoryObj(piID);
if (inventoryObj == null) return answer;
var expPathm = [inventoryObj.id];
expanded.push(expPathm);
getchilds(piID, inventoryObj);
answer.push(inventoryObj);
var result = {
titems: answer,
exp: expanded
};
return result;
function getchilds(sourceID, parentTree) {
var childs = [];
var grPI = new GlideRecord('sn_prd_invt_product_inventory');
if (grPI.get(sourceID)) {
var specObj = getSpecObj(grPI.getValue('specification'));
if (specObj != null) {
childs.push(specObj);
}
}
getCIs(sourceID, childs);
var gr = new GlideRecord('sn_prd_invt_product_inventory');
gr.addQuery('parent_sold_product', sourceID);
gr.query();
while (gr.next()) {
var childID = gr.getValue('sys_id');
var inventoryObj2 = getInventoryObj(childID);
if (level < 2) {
var expPath = [expPathm[0], inventoryObj2.id];
expanded.push(expPath);
}
if (level < MAXLEVEL && hasChildren(childID)) {
level++;
getchilds(childID, inventoryObj2);
level--;
}
childs.push(inventoryObj2);
}
parentTree.children = childs;
}
function getInventoryObj(piID) {
var record = null;
var grSpec = new GlideRecord('sn_prd_invt_product_inventory');
if (grSpec.get(piID)) {
var specLabel = "PI: " + grSpec.getValue('name');
record = {
id: grSpec.getValue('sys_id') + '#sn_prd_invt_product_inventory',
label: specLabel
};
}
return record;
}
function getCIs(piID, childArray) {
var ciItem = null;
var grIBI2SP = new GlideRecord('sn_install_base_m2m_installed_product');
grIBI2SP.addQuery('sold_product', piID);
grIBI2SP.query();
while (grIBI2SP.next()) {
var grIBI = new GlideRecord('sn_install_base_item');
if (grIBI.get(grIBI2SP.getValue('install_base_item'))) {
ciItem = {
id: grIBI.getValue('configuration_item') + '#cmdb_ci',
label: "CI:" + grIBI.getDisplayValue('configuration_item')
};
childArray.push(ciItem);
}
}
}
function getSpecObj(piID) {
var record = null;
var grSpec = new GlideRecord('sn_prd_pm_specification');
if (grSpec.get(piID)) {
var clName = grSpec.getValue('sys_class_name');
var spPrefix = "SP";
if (clName == 'sn_prd_pm_resource_specification') {
spPrefix = 'RS';
} else if (clName == 'sn_prd_pm_product_specification') {
spPrefix = 'PS';
} else if (clName == 'sn_prd_pm_service_specification') {
spPrefix = 'CFSS';
}
var specLabel = spPrefix + ': ' + grSpec.getValue('number') + " " + grSpec.getValue('name');
record = {
id: grSpec.getValue('sys_id') + '#' + clName,
label: specLabel
};
}
return record;
}
// returns number of children
function hasChildren(sourceID) {
var result = false;
var gaRels = new GlideAggregate('sn_prd_invt_product_inventory');
gaRels.addQuery('parent_sold_product', sourceID);
gaRels.addAggregate('COUNT');
gaRels.query();
if (gaRels.next()) {
var cnt = gaRels.getAggregate('COUNT');
if (cnt > 0) {
result = true;
}
}
return result;
}
}
The definition of the output schema:
[
{
"name": "tree",
"label": "Tree Content",
"description": "",
"readOnly": true,
"fieldType": {
"titems": {
"name": "titems",
"label": "Array of Tree Items",
"description": "",
"readOnly": true,
"fieldType": "array"
},
"exp": {
"name": "exp",
"label": "Expanded Items",
"description": "Array of expanded items",
"readOnly": true,
"fieldType": "array"
}
}
}
]
Data binding to the content tree:
I hope that helps you.
I will always try to give a meaningful and valid answer.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-21-2022 09:17 AM
Hi Sergey,
I have used the content tree component already. I'm using the Data Broker Server Script (in navigator called: "Transforms". Don't be confused! ;))
You must prepare an array of object(var answer in my script) with the properties "id" and "label".
For additional hints see my article: UI Builder Component: Content Tree
Here an example to work with different tables in one tree.
the script (it's working with 4 different tables, yours should be a more simple one):
function transform(input) {
var piID = input.productID;
var answer = [];
var expanded = [];
var level = 0;
var MAXLEVEL = 9; // maximum for recursion level
var inventoryObj = getInventoryObj(piID);
if (inventoryObj == null) return answer;
var expPathm = [inventoryObj.id];
expanded.push(expPathm);
getchilds(piID, inventoryObj);
answer.push(inventoryObj);
var result = {
titems: answer,
exp: expanded
};
return result;
function getchilds(sourceID, parentTree) {
var childs = [];
var grPI = new GlideRecord('sn_prd_invt_product_inventory');
if (grPI.get(sourceID)) {
var specObj = getSpecObj(grPI.getValue('specification'));
if (specObj != null) {
childs.push(specObj);
}
}
getCIs(sourceID, childs);
var gr = new GlideRecord('sn_prd_invt_product_inventory');
gr.addQuery('parent_sold_product', sourceID);
gr.query();
while (gr.next()) {
var childID = gr.getValue('sys_id');
var inventoryObj2 = getInventoryObj(childID);
if (level < 2) {
var expPath = [expPathm[0], inventoryObj2.id];
expanded.push(expPath);
}
if (level < MAXLEVEL && hasChildren(childID)) {
level++;
getchilds(childID, inventoryObj2);
level--;
}
childs.push(inventoryObj2);
}
parentTree.children = childs;
}
function getInventoryObj(piID) {
var record = null;
var grSpec = new GlideRecord('sn_prd_invt_product_inventory');
if (grSpec.get(piID)) {
var specLabel = "PI: " + grSpec.getValue('name');
record = {
id: grSpec.getValue('sys_id') + '#sn_prd_invt_product_inventory',
label: specLabel
};
}
return record;
}
function getCIs(piID, childArray) {
var ciItem = null;
var grIBI2SP = new GlideRecord('sn_install_base_m2m_installed_product');
grIBI2SP.addQuery('sold_product', piID);
grIBI2SP.query();
while (grIBI2SP.next()) {
var grIBI = new GlideRecord('sn_install_base_item');
if (grIBI.get(grIBI2SP.getValue('install_base_item'))) {
ciItem = {
id: grIBI.getValue('configuration_item') + '#cmdb_ci',
label: "CI:" + grIBI.getDisplayValue('configuration_item')
};
childArray.push(ciItem);
}
}
}
function getSpecObj(piID) {
var record = null;
var grSpec = new GlideRecord('sn_prd_pm_specification');
if (grSpec.get(piID)) {
var clName = grSpec.getValue('sys_class_name');
var spPrefix = "SP";
if (clName == 'sn_prd_pm_resource_specification') {
spPrefix = 'RS';
} else if (clName == 'sn_prd_pm_product_specification') {
spPrefix = 'PS';
} else if (clName == 'sn_prd_pm_service_specification') {
spPrefix = 'CFSS';
}
var specLabel = spPrefix + ': ' + grSpec.getValue('number') + " " + grSpec.getValue('name');
record = {
id: grSpec.getValue('sys_id') + '#' + clName,
label: specLabel
};
}
return record;
}
// returns number of children
function hasChildren(sourceID) {
var result = false;
var gaRels = new GlideAggregate('sn_prd_invt_product_inventory');
gaRels.addQuery('parent_sold_product', sourceID);
gaRels.addAggregate('COUNT');
gaRels.query();
if (gaRels.next()) {
var cnt = gaRels.getAggregate('COUNT');
if (cnt > 0) {
result = true;
}
}
return result;
}
}
The definition of the output schema:
[
{
"name": "tree",
"label": "Tree Content",
"description": "",
"readOnly": true,
"fieldType": {
"titems": {
"name": "titems",
"label": "Array of Tree Items",
"description": "",
"readOnly": true,
"fieldType": "array"
},
"exp": {
"name": "exp",
"label": "Expanded Items",
"description": "Array of expanded items",
"readOnly": true,
"fieldType": "array"
}
}
}
]
Data binding to the content tree:
I hope that helps you.
I will always try to give a meaningful and valid answer.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-06-2022 10:56 AM
Hi Guido,
Thank you for this information!
I used JavaScript to build the tree but I use it on the Content Tree component level.
Using "Transforms" is more accurate.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-20-2023 12:29 AM
Thanks you for Information,
maybe you can know why when select on row in tree or narrow one of the parents
all the parents become narrowed?

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-25-2023 10:02 AM
The selection of items in the tree is an extra JSON string and can be controlled also by client script.
Maybe that there is such a part of logic in your page.
I will always try to give a meaningful and valid answer.