Linking to application data
Summary
In this chapter, we will walk through how to link our database application data to HOOPS Communicator.
Concepts
Loading the application data
Inserting models into the scene
Retrieve supplemental database information
Now that we have a running WebViewer and have successfully loaded a model into that viewer, we want to bring in the supplemental part data from our “database”. By using identical naming between the model .SCS file and the .JSON database files, we can use the loaded model name to request the part specific database information. It makes sense to do this when we request the model. We can use the then()
method of the returned promise from loadSubtreeFromScsFile()
to fetch our data, as shown below.
this._viewer.model.loadSubtreeFromScsFile(modelNodeId, "/data/" + modelName + ".scs")
.then(() => {
this._viewer.view.fitWorld();
fetch("/data/database/" + modelName + ".json")
.then((resp) => {
if (resp.ok) {
resp.json()
.then((data) => {
});
}
else {
alert("No JSON data for this Model was found.");
}
});
Storing retrieved data
The data
variable contains the NodeData
JSON object we created earlier. In order to minimize the number of asynchronous requests to this database, we will store the data we need in a Map
structure. The keys of the map will be the Node IDs and the values which contain the rest of the data object and its members. We will loop through the array of NodeData
objects, and store each in the Map
.
First let’s define the Map
as a member of our main class.
class main {
constructor() {
// Initialize a Map to quickly reference nodeId -> external app data
// The Map will be populated when a model is loaded
this._modelData = new Map();
// Instantiate the viewer
this._viewer = new Communicator.WebViewer({
containerId: "viewer",
empty: true,
enginePath: "https://cdn.jsdelivr.net/gh/techsoft3d/hoops-web-viewer@latest",
});
Now, back within the loadModel
function and the then()
block of the parsed JSON response, we will clear (if populated already) and populate this Map
. While we are looping through, we will also aggregate the price of all the parts, so we can display the total cost as well.
resp.json()
.then((data) => {
let nodeData = data.NodeData;
let numEntries = nodeData.length;
let clippedID;
let totalCost = 0;
this._modelData.clear();
for (let i = 0; i < numEntries; ++i) {
clippedID = nodeData[i].ID;
this._modelData.set(clippedID, nodeData[i]);
totalCost += nodeData[i].Price;
}
// Display the total cost of the assembly
document.getElementById("inv-total-cost").innerHTML = `$ ${totalCost.toFixed(2)}`;
});
Filling application data
With the new _modelData
map populated, we can use the selectionArray
callback to query our additional part data when a selection is made. The selectionArray
callback will return an array of node IDs when a selection operation is made by the user. Using these node IDs, which are identical to the node IDs in our _modelData
object, we can retrieve the corresponding part data for the selection. Since the selectionArray
callback can contain multiple entries, we will just display the first entry’s information.
selectionArray: (selectionEvents) => {
// Reset info fields if no selection item was chosen
if (selectionEvents.length == 0) {
document.getElementById("node-id").innerHTML = "--";
document.getElementById("node-name").innerHTML = "--";
document.getElementById("inv-manufacturer").innerHTML = "--";
document.getElementById("inv-select-cost").innerHTML = "--";
document.getElementById("inv-total-stock").innerHTML = "--";
document.getElementById("inv-avail-stock").innerHTML = "--";
document.getElementById("inv-claimed").innerHTML = "--";
document.getElementById("inv-location").innerHTML = "--";
return;
}
// Otherwise, display node information for the first node in the selection array
const nodeId = selectionEvents[0].getSelection().getNodeId();
document.getElementById("node-id").innerHTML = nodeId.toString() || "Unknown";
document.getElementById("node-name").innerHTML = this._viewer.model.getNodeName(nodeId) || "Node Name Not Defined";
// If the selection nodeId is found in the application data, populate the inspector fields
if (this._modelData.has(nodeId)) {
let nodeData = this._modelData.get(nodeId);
document.getElementById("inv-manufacturer").innerHTML = nodeData.Manufacturer;
document.getElementById("inv-select-cost").innerHTML = `$ ${nodeData.Price.toFixed(2)}`;
document.getElementById("inv-total-stock").innerHTML = nodeData.Stock.toString();
document.getElementById("inv-avail-stock").innerHTML = (nodeData.Stock - nodeData.Reserved).toString();
document.getElementById("inv-claimed").innerHTML = nodeData.Reserved.toString();
document.getElementById("inv-location").innerHTML = nodeData.Location;
}
}
If we reload our application, load the “microengine” or “Moto” model, and select a part on the model, we will see the inspector fields populate. Selecting other parts (or no part at all), updates this information based on the selection. We have successfully brought in external application data and showed an example of linking this data with node IDs.