Our great sponsors
-
We merge SVGs by appending all of elements in our accessory SVG to the
element in our base ReplBot SVG, and all of the accessory's
and
elements to the SVG's main body. This is sufficient for the collection of SVGs we're using for this tutorial, but other SVGs may have additional elements that you'll need to account for.
Finally, we'll add the following code that uses
insertAdjacentHTML()
to add details about the ReplBot's ID and generation before returning the finalbotSvg
in all its glory. Add the following code just below the code above:
// add ID and generation details botSvg.insertAdjacentHTML("beforeend", `ID: ${tokenId}`); botSvg.insertAdjacentHTML("beforeend", `Gen: ${details.parentage[0]}`); return botSvg;
Enter fullscreen mode Exit fullscreen modeConnecting the UI
We've written all of our app functionality – now we just need to make it usable by binding our HTML interface to our JavaScript functions. Add the following function definition to
App.js
, just below the definition ofswitchToReplitTestnet
:
// interface bindEvents: function () { // mint const mintButton = document.getElementById("mint"); mintButton.addEventListener("click", () => { App.mintReplBot(); }); // breed const breedForm = document.getElementById("breed"); breedForm.addEventListener("submit", (event) => { event.preventDefault(); App.breedReplBot(breedForm.elements['parentOneId'].value, breedForm.elements['parentTwoId'].value); }); },
Enter fullscreen mode Exit fullscreen modeThis code will trigger
mintReplBot()
when the mint button is pressed, andbreedReplBot()
when the ReplBot breeding form is submitted. We'll invoke this function ininit
, by adding the following:
init: async function() { if (window.ethereum) { await window.ethereum.request({ method: 'eth_requestAccounts' }); window.web3 = new Web3(window.ethereum); // Switch networks App.switchToReplitTestnet(); // Interface with contract App.replbotContract = new web3.eth.Contract(replbotABI, App.replbotAddress); } App.bindEvents(); // <-- NEW LINE },
Enter fullscreen mode Exit fullscreen modeNext, let's create a function to display our NFT collection. This function will retrieve a list of the current user's NFTs and create an SVG for each of them. Add the following code below the definition of
bindEvents
:
populateCollection: async function() { // get bot IDs let botIds = await App.getMyReplBotIds(); // get container let botContainer = document.getElementById("bots"); botContainer.innerHTML = ""; // clear current content // create bot SVGs botIds.forEach((id) => { App.createReplBotSVG(id).then(result => { botContainer.appendChild(result); }); }); },
Enter fullscreen mode Exit fullscreen modeWe'll want to call this function periodically to keep our ReplBot collection up to date. We can use JavaScript's
setInterval
for this. Add a new line to the definition ofbindEvents
as below:
// interface bindEvents: function () { // mint const mintButton = document.getElementById("mint"); mintButton.addEventListener("click", () => { App.mintReplBot(); }); // breed const breedForm = document.getElementById("breed"); breedForm.addEventListener("submit", (event) => { event.preventDefault(); App.breedReplBot(breedForm.elements['parentOneId'].value, breedForm.elements['parentTwoId'].value); }); // show collection setInterval(App.populateCollection, 5000); // <-- new line },
Enter fullscreen mode Exit fullscreen modeThis will work, but it will recreate all of our SVGs once per second, which will usually be more than we need, and will cause the page to flash constantly. Let's add some caching to prevent this. First, we'll define a new attribute near the top of our
App
definition:
App = { replbotAddress: "YOUR-CONTRACT-ADDRESS", replbotContract: null, ownedReplBots: [], // <-- NEW .... }
Enter fullscreen mode Exit fullscreen modeThis attribute will store our list of ReplBot IDs. Every time we call
populateCollection
, we'll first check whether the list we get from the contract matches our local list. If it does, we'll skip the rest of the function, otherwise, we'll update the cache and continue. Add the following new code to thepopulateCollection
function:
populateCollection: async function() { // get bot IDs let botIds = await App.getMyReplBotIds(); // === NEW CODE BELOW === // check cache if (compareArrays(botIds, App.ownedReplBots)) { return; // array is unchanged } else { App.ownedReplBots = botIds.slice(); // update cache and continue } // === NEW CODE ABOVE === // get container let botContainer = document.getElementById("bots"); botContainer.innerHTML = ""; // create bot SVGs botIds.forEach((id) => { App.createReplBotSVG(id).then(result => { botContainer.appendChild(result); }); }); },
Enter fullscreen mode Exit fullscreen modeNote the use of
slice()
– this will ensure thatownedReplBots
contains a copy ofbotIds
rather than a reference to it.JavaScript does not have a built-in
compareArrays()
function, so we'll need to define one. Navigate to the top offrontend/app.js
and add the following code below the definition offetchSvg()
:
function compareArrays(array1, array2) { // arrays must be same length if (array1.length !== array2.length) { return false } // arrays must be sorted let array1Sorted = array1.slice().sort(); let array2Sorted = array2.slice().sort(); // all values must match for (let i = 0; i < array1.length; i++) { if (array1Sorted[i] !== array2Sorted[i]) { return false; } } return true; }
Enter fullscreen mode Exit fullscreen modeThis function checks that our two arrays are the same length, sorts them and then compares each value. If a single value is different, it returns false, otherwise, it returns true.
Running our application
To run our frontend, we'll create a simple Node.js Express application. Create a file named
frontend.js
at the home directory of your repl and add the following code to it:
const express = require('express'); const app = express(); const PORT = 433; app.use(express.static('frontend')); app.listen(PORT, () => console.log(`Server listening on port: ${PORT}`));
Enter fullscreen mode Exit fullscreen modeThis script will serve the files we created in
frontend
. To make it execute when we run our repl, make sure that config files are showing and open.replit
.In
.replit
, replace the linerun = "node tools"
with the following:
run = "node frontend.js"
Enter fullscreen mode Exit fullscreen modeRun your repl. You should now see your frontend. If you've minted any ReplBot NFTs already, those will also show up.
Where next?
In this two-part tutorial, we've built a fully functional decentralised application, but there's a lot more you can do to improve it. Some suggestions:
- Implement an interface for viewing ReplBot NFTs owned by other users.
- Implement an interface for transferring ReplBot tokens to other addresses.
- Alter the breeding form to show drop-down menus of the user's ReplBots.
- Use the contract event log to track ReplBot creations and births.
- Change the dapp's stylesheet.
- Implement frontend components for any of the new functions you added to the contract after completing part one!
Your can find our repl here.
Related posts
- Torrenting movies with shortcuts using some nodejs apis I host on my raspberry pi
- Can I import data from a website to my QGIS project?
- Setting up & serving static files in ExpressJS
-
Express VS frapi - a user suggested alternative
2 projects | 20 Jun 2022
- Open-Source Contribution Guide : Express NodeJs