##############
Loading models
##############

Summary
=======

In this chapter, we will provide code for you to load and add models to your application.

Concepts
========

* Creating model thumbnail UI elements
* Inserting models into the scene
* Encapsulate event listeners

------------------------------

Set event callbacks
===================

In the previous section, we waited for the :js:attr:`~Communicator.CallbackMap.modelStructureReady` callback to fire before enabling the "Load Model" input button. We can now put some functionality behind this input button. This section of code has quite little to do with HOOPS Communicator, so we will not delve too deeply into it, but of course it is provided below. The idea is that we will create a file input element and listen for the ``onchange`` call when it is clicked. This will recognize a new file has been uploaded. We will then create the necessary thumbnail element, check if there is a model thumbnail image present, and set it in the thumbnail if found. We will then make the thumbnail clickable, and when clicked, it will call a modelLoad function we will write.


.. code-block:: js

	document.getElementById("open-model-button").onclick = () => {
		// Proxy to override the default behavior of file input type
		document.getElementById('file-input').click();
	};
	document.getElementById("file-input").onchange = (e) => {
		// Once a file has been selected by the user, use the file information to 
		// gather the associated relevant data like thumbnails
		let fileChoice = e.target.value;
		let filename = fileChoice.replace(/^.*[\\\/]/, '');
		let modelThumbnail = document.createElement('a');
		let modelname = filename.split(".", 1)[0];
		modelThumbnail.id = modelname;
		modelThumbnail.href = "";
		modelThumbnail.className = "model-thumb";
		modelThumbnail.setAttribute("model", modelname);
		let imgPath = "/data/thumbnails/" + modelname + ".png";
		// Check to see if the selected model has a corresponding thumbnail made
		fetch(imgPath)
			.then((resp) => {
			if (resp.ok) {
				let modelImg = document.createElement('img');
				modelImg.src = imgPath;
				modelThumbnail.appendChild(modelImg);
			}
			else {
				modelThumbnail.innerHTML = modelname;
				console.log("No Image for this Model was found.");
			}
		});
		document.getElementById("models-scroller").appendChild(modelThumbnail);
		// Now update the event callbacks for the thumbnails
		const thumbnailElements = document.getElementsByClassName("model-thumb");
		for (let thumbnail of thumbnailElements) {
			let thumbnailElement = thumbnail;
			thumbnailElement.onclick = (e) => {
				e.preventDefault();
				let elem = e.currentTarget;
				let modelToLoad = elem.getAttribute("model");
				// Load the model into the scene when clicked
				this.loadModel(modelToLoad);
			};
		}
		;
	};


Loading the model into the assembly tree
========================================

Since we will potentially be loading an arbitrary number of models in our application, we can abstract the loading into its own ``loadModel`` function, as a part of the main class. The ``loadModel`` function will take one parameter – the model name to be loaded.

Within the function, we want to be sure we are adding our model to the model tree in a smart manner. Much like in the Getting Starting guide, we will create a new model node off the root node, name it, and load our model to that node. First, we must clear the current model state, so we can then load a new model. We can use the promise returned from the :js:meth:`~Communicator.Model.clear` command to then continue to load our model. Please note that in an SCS workflow, you cannot use the :js:meth:`~Communicator.Model.switchToModel` API on the :doc:`Model </api_ref/viewing/classes/Communicator.Model>` class – this will only work in a Stream Cache Server workflow.

We also want to ensure that our camera is fit to the model boundaries, so we will use the ``then()`` method on the returned promise from :js:meth:`~Communicator.Model.loadSubtreeFromScsFile` to fit the camera.

Lastly, since we will have the model loaded, we can use HOOPS Communicator to query the file type and model name, and populate those HTML elements in our template.

.. code-block:: js

	loadModel(modelName) {
		this._viewer.model.clear()
			.then(() => {
			const nodeName = "Model-" + modelName;
			const modelNodeId = this._viewer.model.createNode(null, nodeName);
			this._viewer.model.loadSubtreeFromScsFile(modelNodeId, "/data/database/" + modelName + ".scs")
		  .then(() => {
			this._viewer.view.fitWorld();
		   // Get and set the rest of the model level info
		   const modelRoot = this._viewer.model.getNodeChildren(modelNodeId)[0];
		   const modelFileName = this._viewer.model.getModelFileNameFromNode(modelRoot);
		   const modelFileFormat = this._viewer.model.getModelFileTypeFromNode(modelRoot);
		   document.getElementById("model-file-name").innerHTML = modelFileName || "N/A";
		   document.getElementById("model-file-type").innerHTML = Communicator.FileType[modelFileFormat] || "N/A";
		  });
		});
	}


Encapsulate the event listeners
===============================

We now have the two pieces we need to load models, we just need to set the callbacks in the application. Since we will have several UI elements we need to hook up when the application loads, we will create a function called ``setEventListeners`` with our event listener code, and call this function in the application constructor.

.. code-block:: js

	setEventListeners() {
	document.getElementById("open-model-button").onclick = () => {
		// Proxy to override the default behavior of file input type
		document.getElementById('file-input').click();
	};
	document.getElementById("file-input").onchange = (e) => {
		// ...
	}

In the constructor, you can make a call to the ``setEventListeners`` function.

.. code-block:: js

	this.setEventListeners();

Once you have placed your event listeners within the ``setEventListeners`` function, and called this function in your ``main`` constructor, run your application and try to load the "Moto" model. Remember that it will populate a thumbnail with your model image first, and clicking on that model thumbnail will actually load it into the viewer.

If you take a look over at our application, you will see the viewer with the model loaded and rendered!

.. image:: images/inventory-mgmt-moto.png

You should also now be able to navigate and make selections on the model within the :doc:`WebViewer </api_ref/viewing/classes/Communicator.WebViewer>` component. If you would like, feel free to load more models in the sidebar and click between the thumbnails to see how the application switches between models.
