Search in Custom UI
This tutorial describes how to locate parts of a model using the Search extension with a custom-built UI. We will use the JavaScript library to access the search capabilities.
For details about using the Search extension with the default built-in Viewer UI, see the Search in Default UI tutorial.
Note that this tutorial focuses on the Search extension and does not describe how to style your UI.
Before You Begin
- Register an app.
- Acquire an OAuth token with
viewables:read
anddata:write
scopes. - Upload a file to the Forge Object Storage Service (OSS), as described in the File Upload tutorial.
- Prepare a source file for viewing by encoding the source URN to base64 format and translating the file to SVF format, as described in the Prepare a File for the Viewer tutorial.
- Acquire a new OAuth token with ONLY
viewables:read
scope so that it can pass to the front-end JavaScript client.
Step 1: Prepare the Viewer HTML
Create a file called in-viewer-search.html, and copy in the following code.
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
<!-- The Viewer CSS -->
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/2.*/style.min.css" type="text/css">
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/2.*/A360.css" type="text/css">
<!-- Developer CSS -->
<style>
#in-viewer-search {
position: absolute;
left: 283px;
color: white;
background-color: black;
opacity: 0.5;
padding: 20px;
height: 445px;
overflow: auto;
}
.hidden {
display: none;
}
.result-block {
padding: 5px;
}
.load-more {
cursor: pointer;
}
#in-viewer-search ul.result-list {
padding-left: 5px;
}
</style>
</head>
<body>
<div id="MyViewerDiv"></div>
<div id="in-viewer-search">
<input type="search" disabled="true" id="searchbar" /> <input type="button" disabled="true" id="search" value="search" onclick="search()" />
<div id="this-view" class="result-block">
<div id="this-view-no-res" class="hidden">No results for loaded model search.</div>
<div id="this-view-results" class="hidden">
<label id="qtyv"></label><label> results for loaded model search</label>
<ul id="this-view-res" class="result-list">
</ul>
</div>
</div>
<div id="this-item" class="result-block">
<div id="this-item-no-res" class="hidden">No results for the related items.</div>
<div id="this-item-results-main" class="hidden">
<label id="qtyi"></label><label> results for related items search</label>
<div id="this-item-results" class="result-list"></div>
</div>
</div>
</div>
<!-- The Viewer JS -->
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/2.*/three.min.js"></script>
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/2.*/Autodesk360App.js"></script>
<!-- Developer JS -->
<script>
var viewerApp;
var options = {
env: 'AutodeskProduction',
accessToken: '<YOUR_APPLICATION_TOKEN>'
};
var documentId = 'urn:<YOUR_URN_ID>';
Autodesk.Viewing.Initializer(options, onInitialized);
function onInitialized() {
var config = {};
config.extensions = ["Autodesk.InViewerSearch"]; //load in-viewer-search
config.inViewerSearchConfig = {
uiEnabled: false,
relatedItemsTab:{
enabled: true, //If false, the tab is hidden.
displayName: 'This Item',
pageSize: 20
},
loadedModelTab: {
enabled: true, //If false, the tab is hidden.
displayName: 'This View',
pageSize: 50
}
};
viewerApp = new Autodesk.Viewing.A360ViewingApplication('MyViewerDiv', options);
viewerApp.registerViewer(viewerApp.k3D, Autodesk.Viewing.GuiViewer3D, config);
viewerApp.loadDocumentWithItemAndObject(documentId);
}
/* add custom script function here */
</script>
</body>
</html>
Replace YOUR_APPLICATION_TOKEN
and YOUR_URN_ID
with the application token generated when the app was created, and the base64 encoded source URN.
Note that uiEnabled
is set to false
to disable the UI for the inViewerSearch object. See the JavaScript Library for details about other configurable options for the Search extenstion.
Step 2: Load the Extension
Instruct ViewingApplication
to load the inViewerSearch extension.
var api = viewerApp.getCurrentViewer().getExtension("Autodesk.InViewerSearch");
Step 3: Wait for the Extension to Initialize
Add a listener to determine when the inViewerSearch extension is initialized.
inViewerSearch = viewer.getExtension("Autodesk.InViewerSearch");
inViewerSearch.addInitListener(function() {
initialized = true;
document.getElementById("searchbar").disabled = false;
document.getElementById("search").disabled = false;
});
Step 4: Perform a Search
This example searches both the property values in the current model that is loaded in the Viewer (Current Model search), as well as the property values for all models in the document object (Document search).
Once the searches are complete the callbacks process the results and add them to the display page.
function search() {
var li, elem;
if (!initialized) {
return;
}
var str = document.getElementById("searchbar").value;
inViewerSearch.searchModel(str, {}, function(results) {
li = "";
if (results.resultCount > 0) {
var list = document.getElementById("this-view-res");
list.innerHTML = "";
document.getElementById("this-view-results").classList.remove("hidden");
elem = document.getElementById("this-view-no-res");
if (elem.className.indexOf("hidden") < 0) {
elem.classList.add("hidden");
}
// Display the number of results.
document.getElementById("qtyv").innerText = results.resultCount;
// Add results to the list.
results.results.forEach(function(result){
li = li + "<li>" + result.nodeName + "</li>";
});
// Add a link to request additional results.
if (results.moreResults) {
li = li + "<li class=\"load-more\" onclick='loadMoreModelSearchResults(\"" + str + "\",2)'>Load more...</li>";
}
// Display on the page.
list.innerHTML = li;
} else {
// Display a message to notify that there are no results.
document.getElementById("this-view-no-res").classList.remove("hidden");
elem = document.getElementById("this-view-results");
if (elem.className.indexOf("hidden") < 0) {
elem.classList.add("hidden");
}
}
});
inViewerSearch.searchDocument(str, {}, function(resultsItem) {
var ul = "",
elem;
if (resultsItem.resultCount > 0) {
var resHtml = document.getElementById("this-item-results");
resHtml.innerHTML = "";
document.getElementById("this-item-results-main").classList.remove("hidden");
elem = document.getElementById("this-item-no-res");
if (elem.className.indexOf("hidden") < 0) {
elem.classList.add("hidden");
}
// Display the number of results.
document.getElementById("qtyi").innerText = resultsItem.resultCount;
// Add results grouped by model.
resultsItem.results.forEach(function(resultsGeometry) {
ul = ul + "<div class='geometry-res'><label>" + resultsGeometry.name + "</label><ul id='geometry_" + resultsGeometry.id + "' class='result-list'>";
// Add results to the list.
resultsGeometry.results.forEach(function(result) {
ul = ul + "<li>" + result.nodeName + "</li>";
});
// Add a link to request additional results.
if (resultsGeometry.moreResults) {
ul = ul + "<li class=\"load-more\" onclick='loadMoreDocumentSearchResults(\"" + str + "\",2, \""+ resultsGeometry.id +"\")'>Load more...</li>";
}
ul = ul + "</ul></div>";
});
// Display on the page.
resHtml.innerHTML = ul;
} else {
// Display a message to notify that there are no results.
document.getElementById("this-item-no-res").classList.remove("hidden");
elem = document.getElementById("this-item-results-main");
if (elem.className.indexOf("hidden") < 0) {
elem.classList.add("hidden");
}
}
});
}
Step 5: Configure Multiple Pages of Results
If the results do not fit on one page, you need to set up additional requests to retrieve the rest of the results.
The following code sets up requests to retrieve results for the Current Model search.
function loadMoreModelSearchResults(str, page) {
inViewerSearch.searchModel(str, { page: page }, function(results) {
var list = document.getElementById("this-view-res");
list.lastChild.remove();
document.getElementById("this-view-results").classList.remove("hidden");
results.results.forEach(function(r) {
var li = document.createElement("li");
li.innerText = r.nodeName;
list.appendChild(li);
});
if (results.moreResults) {
var li = document.createElement("li");
li.className = "load-more";
li.innerText = "Load more...";
li.onclick = function(){
loadMoreModelSearchResults(str, page+1);
};
list.appendChild(li);
}
});
}
The following code sets up requests to retrieve results for the Document search.
function loadMoreDocumentSearchResults(str, page, geom) {
inViewerSearch.searchDocument(str, {page: page, geometryId: geom}, function(resultsItem) {
var list = document.getElementById("geometry_" + geom);
list.removeChild(list.lastChild);
resultsItem.results.forEach(function(r) {
r.results.forEach(function(i) {
var li = document.createElement("li");
li.innerText = i.nodeName;
list.appendChild(li);
});
if (r.moreResults) {
var li = document.createElement("li");
li.className = "load-more";
li.innerText = "Load more...";
li.onclick = function() {
loadMoreDocumentSearchResults(str, page+1, geom);
};
list.appendChild(li);
}
});
});
}
For details about understanding the search results, see the Search in Default UI tutorial.