Highlight Selected Items
1. To provide clear visual feedback to the runtime user, selecting a sub-assembly or part item in the model should trigger a visual indicator. This helps users confirm that they are viewing data for the correct item. To achieve this, we will apply the highlight shader to the selected model item. Add the following to Visualization.js:
// call this function to highlight the list of selected parts
//
var highlightParts = (selectedList, modelId) => {

var highlightedList = [];

// run over all the items in the list and assign the highlight shader
selectedList.forEach( (partId) => {

var renderingPartId = modelId + "-" + partId;
var shader = "highlight;r f 1;g f 0.5;b f 0.25;a f 1" + (twx.app.isPreview() ? ";virtualMode f 1.0":"");
tml3dRenderer.setProperties(renderingPartId, { shader: shader, opacity: 1.0, hidden: false });

// and keep a record of what we highlighted - so that we wan undo it later
//
highlightedList.push(renderingPartId);

});

return highlightedList;
}
2. Additionally, when the user clicks away from a previously selected sub-assembly or selects a different one, the highlight shader must be updated accordingly. This ensures that only the currently selected item remains visually emphasized, maintaining clarity throughout the interaction. Therefore, you’ll also add the following to Visualization.js:
// call this function to clear the highlights
//
var highlightReset = (highlightedList) => {

// run over all the items in the list and unsasign the shader
//
highlightedList.forEach( (modelIdpartId) => {

tml3dRenderer.setProperties(modelIdpartId,{ shader:"", opacity: 0.2, hidden: !twx.app.isPreview() });

});
return [];
}
3. Now, when a user selects an item from the part list, that item should be highlighted. Pay close attention to the logic used in the following code snippet—especially if you're working with your own model. The logic relies on attributes that must be defined and available in Creo Illustrate and loaded at runtime for each part. If you're using a different key or custom attributes to display part-specific data, be sure to update the attribute references in the following code accordingly to match your model's structure in Visualization.js:
// used to track which items are highlighted
$scope.lit = [];


// called when the user taps on a property in the named list, or taps on a part in the view - this changes the 'current' field
//
$scope.$watch(

() => {

return ($scope.view.wdg.partsList.list != undefined &&
$scope.view.wdg.partsList.list.current != undefined) ? JSON.stringify($scope.view.wdg.partsList.list.current) : "";
},
//value == 'currentSelectedPart',
(value) => {

highlightReset($scope.lit);

var partNumber = JSON.parse(value);

// first of all, lets get all the metadata values for this selected item
//
var plist = [];
PTC.Metadata.fromId(partNumber.model)
.then ( (metadata) => {

// find all the items with this partnumber
//
plist = metadata.find(metadataKey).like(partNumber.name);

if (plist == undefined)
{
return;
}

// highlight those items
//
$scope.lit = highlightParts(plist.getSelected(), partNumber.model);

// now find some interesting properties
//
var interestingProperties = ["cost","supplier","weight","partNumber"];

// this function (declared inline) is used to get the property values (see above) for eeach of the items
// in the previously acquired partnmubered plist - it will iterate through each item (idpath) and get the
// properties for each item; if found, they are captured into an array of name=value pairs.
//
var getInteresting = (ip) => {
var ilist = ip;

return function(idpath) {
const res = this.get(idpath, ilist);

var retobj = { path:idpath };

if (res != undefined && res.length === ilist.length) for (var p=0;p<ilist.length;p++) {

// add each result as a new property in the return body
retobj[ilist[p]] = res[p];
}

return retobj;
}
}

// get a list of items with this property set
//
plist = plist.getSelected(getInteresting(interestingProperties));

// helper function to get and format the properties - this generates a list of name|value pairs
//
var getNameValues = (items) => {
var nv = [];
items.forEach( (i) => {
Object.keys(i).forEach( (j) =>{
if (interestingProperties.includes(j)) nv.push({name:j, value:i[j]});
})
})
return nv;
}

// we need to turn this into a list of item/name/value
//
$scope.view.wdg.propertiesList.data = getNameValues(plist);
//
// and show the popup
//
twx.app.fn.triggerWidgetService("propertyPopup", 'showpopup')

})
}
);
4. In cases where the user does not have the specific part name or ID, they may interactively select the relevant sub-assembly directly on the model. For this scenario, add the following:
// called if the user taps on a part
$scope.$on("userpick", (event, model, type, args) => {

PTC.Metadata.fromId(model)
.then ( (metadata) => {

var pathId = JSON.parse(args).occurrence;

var sid = metadata.get(pathId, metadataKey);

var findMetaDataKey = (p) => {
// simple inline closure that will test if the item name is the value specificed (p in this case)
// this gets called for all items in the list (see findIndex, below)
//
return function(v) { return v.name == p; }
}

// work out which row we need to select
//
var fid = $scope.view.wdg.partsList.list.findIndex(findMetaDataKey(sid));

// return call here if not found

// ... and mark it as selected
//
$scope.view.wdg.partsList.list.forEach( (v,i) => { v._isSelected = (i == fid) });

// and get the row to highlight in 3d
//
$scope.view.wdg.partsList.list.current = $scope.view.wdg.partsList.list.filter( (v,i) => { return i == fid })[0];
})
});
5. In the last section, you’ll build the property popup.
Was this helpful?