{"id":445,"date":"2017-07-18T14:12:23","date_gmt":"2017-07-18T13:12:23","guid":{"rendered":"http:\/\/www.troliver.com\/?p=445"},"modified":"2017-07-18T14:12:23","modified_gmt":"2017-07-18T13:12:23","slug":"feeding-the-frontend-displaying-data-with-d3-js-part-2","status":"publish","type":"post","link":"https:\/\/www.troliver.com\/?p=445","title":{"rendered":"Feeding the frontend &#8211; displaying data with D3.js Part 2"},"content":{"rendered":"<p>Carrying straight on from\u00a0<a href=\"http:\/\/www.troliver.com\/?p=434\">my previous post<\/a>\u00a0on the subject, I&#8217;m going to go through the alluded to third page to add, which is\u00a0<em>query.php<\/em> and change\u00a0<em>main.js<\/em> a little, too. The purpose is to swap out the hard coded arrays of data for an external data source, namely a web-based resource rather than any locally stored files using <span style=\"color: #000000;\"><b>AJAX\u00a0<\/b><\/span>and\u00a0<strong>JSON<\/strong>.<\/p>\n<h2>Query.php<\/h2>\n<p>What we&#8217;ve got so far are some blobs being drawn at various positions on the screen, based on the data stored in a couple of arrays. It isn&#8217;t all that exciting, but the main thing is that we have data in an array that can be visualised. The next step is then to load in that list of a computers from an external source. D3 can do things like load CSVs and JSON data in from a file, but since I&#8217;ve been using php to fetch data already from my database, I felt that it would be worth just adding a bit more code to what is already there to put that data into d3.<\/p>\n<p><a href=\"http:\/\/www.troliver.com\/?p=372\">In another previous post<\/a>\u00a0I&#8217;d displayed the results of a query in a table, but what I want to do instead is to store them in JSON format. Its a data format that can be read and used by many different parsers on different platforms as well as being able to be directly read in by Javascript and interpreted as a list of objects.<\/p>\n<p>Below is what you should be able to use to return and display a JSON string.<\/p>\n<pre class=\"lang:default decode:true\">&lt;?php \r\n\t\r\n\t$username = \"user\";\r\n\t$password = \"password\";\r\n\t$room = $_GET['room'];\r\n\r\n\t$serverDB = \"mysql:host=localhost;dbname=inventory\";\r\n\t$conn = new PDO($serverDB, $username, $password);\r\n\r\n\tif ($conn-&gt;connect_error) {\r\n\t\tdie(\"Connection failed: \" . $conn-&gt;connect_error);\r\n\t}\r\n\t\t\r\n\t$getHosts = \"SELECT * from hosts WHERE Room = '\". $room .\"'\";\r\n\r\n\t$hosts = array();\r\n\t\r\n\tif ($result = $conn-&gt;query($getHostsPlaces))\r\n\t{\r\n\twhile ($row = $result-&gt;fetch(PDO::FETCH_ASSOC))\r\n\t\t{\r\n\t\t\t$hosts[] = $row;\r\n\t\t}\r\n\t\t\r\n\techo json_encode($hosts);\r\n\t}\r\n\t\r\n\t$result-&gt;closeCursor();\r\n\t$conn = null;<\/pre>\n<p>The first thing to note here is that, in this example, the request for the page will take the form of http:\/\/url\/query.php<strong>?room=A001 <\/strong>&#8211; where &#8220;A001&#8221; would be the room you are asking to have the computers returned from. This lets me create the select statement with the room specified and return only the computers in a certain room.<\/p>\n<p>I then create an array that has a new element added for each\u00a0<em>row<\/em> (leftover terminology from the previous example). The syntax isn&#8217;t obvious but assinging a value to an array without specifying a key seems to be the same as <em>array.push()<\/em> in Javascript. After that, I then call\u00a0<em>json_encode\u00a0<\/em>and pass it the array of hosts, which serialises the data to be read in by something else later. In this instance I simply echo the data, which should be an array of data.<\/p>\n<p>One last thing is that, unlike any previous examples, I try to now use PDOs. Included with most php\/mysql installations of Linux, its a technology-neutral way to access most databases (although the connection string does specify that it is a MYSQL database), so in lieu of any real reason not to, I have decided to go down that route. However, one important thing I found out was that, if you don&#8217;t properly close the connection and queries when you&#8217;re done, you get internal server errors. So, whilst you didn&#8217;t have to do this with mysql(i) connections, you absolutely\u00a0<em>do\u00a0<\/em>have to with PDOs, which is probably a good thing.<\/p>\n<p>When I call this page from my browser, what is displayed is the JSON&#8217;d data, as expected:<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-442\" src=\"http:\/\/www.troliver.com\/wp-content\/uploads\/2017\/07\/phpreturn.png\" alt=\"\" width=\"665\" height=\"168\" srcset=\"https:\/\/www.troliver.com\/wp-content\/uploads\/2017\/07\/phpreturn.png 665w, https:\/\/www.troliver.com\/wp-content\/uploads\/2017\/07\/phpreturn-300x76.png 300w\" sizes=\"(max-width: 665px) 100vw, 665px\" \/><\/p>\n<p>With that done, I can now go back to my main.js file and make some changes so that this data is read in.<\/p>\n<h2>main.js<\/h2>\n<h3>jQuery and d3.json<\/h3>\n<p>It is at this point that the reference to the <em>jQuery<\/em> library becomes relevant. To access the jQuery object, you just have to use a $. It turns out that this is actually a legitimate variable name &#8211; but thankfully nobody else would be crazy enough to use it on its own so jQuery can use it all on its own. What it can then be used to do is to manipulate the DOM, like D3, although in a different way. But it can also be used to perform an\u00a0<em>AJAX<\/em> request &#8211; used to grab data from another page via an XML HTTP Request, but with a fraction of the code. To call our <em>query.php<\/em> page and interpret the returned data as JSON, you can use the following code below.<\/p>\n<pre class=\"lang:js decode:true\">$.ajax({\r\n\turl: \"query.php?room=w004\",\r\n\tdataType: \"JSON\",\r\n\tsuccess: makeComputers\r\n});<\/pre>\n<p>The\u00a0<em>callback<\/em> function is what is invoked (with the data passed in as a parameter) when the AJAX call is successful (you can create a similar callback for a failure, too). I&#8217;ve made a function called\u00a0<em>makeComputers<\/em>, where I will put all of the previous code.<\/p>\n<p><em>Update<\/em>: I have since found that there is an even simpler way, when using\u00a0<em>d3.xhr<\/em> or\u00a0<em>d3.json<\/em>. For this example, you can reduce the above code down to a single line and cut <em>jQuery<\/em> out completely:<\/p>\n<pre class=\"lang:default decode:true\">d3.json(\"query.php?room=w004\", makeComputers);<\/pre>\n<p>In either case, the result will be the same; makeComputers will be called when the call has completed. This is an <em>asynchronous <\/em>call, though, so any code inside\u00a0<em>makeComputers<\/em> will likely happen a few milliseconds after whatever follows either call.<\/p>\n<p>I&#8217;ve modified the original code from last time to be as follows:<\/p>\n<pre class=\"lang:js decode:true\">function Computer(place, hostname) {\r\n    this.place = place;\r\n    this.hostname = hostname;\r\n}\r\nfunction Position(place, posx, posy) {\r\n    this.place = place;\r\n    this.posx = posx;\r\n    this.posy = posy;\r\n}\r\n\r\nvar positions = [\r\n    new Position('1', 0, 0),\r\n    new Position('2', 80, 0),\r\n    new Position('3', 160, 0),\r\n    new Position('4', 240, 0),\r\n    new Position('5', 320, 0),\r\n    new Position('6', 400, 0),\r\n    new Position('7', 0, 100),\r\n    new Position('8', 80, 100),\r\n    new Position('9', 160, 100),\r\n    new Position('10', 240, 100),\r\n    new Position('11', 320, 100),\r\n    new Position('12', 400, 100),\r\n    new Position('13', 0, 200),\r\n    new Position('14', 80, 200),\r\n    new Position('15', 160, 200),\r\n    new Position('16', 240, 200),\r\n    new Position('17', 320, 200),\r\n    new Position('18', 400, 200),\r\n    new Position('19', 0, 300),\r\n    new Position('20', 80, 300),\r\n    new Position('21', 160, 300),\r\n    new Position('22', 240, 300),\r\n    new Position('23', 320, 300),\r\n    new Position('24', 400, 300),\r\n];\r\n\r\nvar computers = [];\r\n\r\nvar w = 450;\r\nvar h = 450;\r\nvar svge = d3.select(\"body\")\r\n            .append(\"svg\")\r\n            .attr(\"width\", w)\r\n            .attr(\"height\", h);\r\nvar div = d3.select(\"body\")\r\n            .append(\"div\")\r\n            .attr(\"class\", \"tooltip\")\r\n            .style(\"opacity\", 0)\r\n            .text(\"Tooltip\");\r\n\r\nfunction makeComputers(jsony){\r\n\tfor(var k=0; k&lt;jsony.length; k++){\r\n\t\tcomputers.push(new Computer(k+1,jsony[k]['hostname']));\r\n\t}\r\n\tvar recties = svge.selectAll(\"rect\")\r\n\t\t\t.data(computers)\r\n\t\t\t.enter()\r\n\t\t\t.append(\"svg\")\r\n\t\t\t.attr(\"data-hello\", function (d,i) {return d[i]; })\r\n\t\t\t.append(\"rect\")\r\n\t\t\t.attr(\"width\", 30)\r\n\t\t\t.attr(\"height\", 30)\r\n\t\t\t.attr(\"x\", function(d,i) {var xloc = positions.findIndex(y =&gt; y.place == computers[i].place); return positions[xloc].posx +15})\r\n\t\t\t.attr(\"rx\", 6)\r\n\t\t\t.attr(\"ry\", 6)\r\n\t\t\t.attr(\"y\", function(d,i) {var xloc = positions.findIndex(y =&gt; y.place == computers[i].place); return positions[xloc].posy +15})\r\n\t\t\t.style(\"fill\", \"Lavender\")\r\n\t\t\t.on(\"mouseover\", fadein)\r\n\t\t\t.on(\"mouseout\", fadeout)\r\n\t\t\t.on(\"mousemove\", moviemouse);\r\n}\r\nfunction fadein(d, i) {\r\n    div.transition().duration(200).style(\"opacity\", 0.9);\r\n    div.style(\"left\", d3.mouse(this)[0])\r\n\t.style(\"top\", d3.mouse(this)[1])\r\n\t.html(d.hostname);\r\n}\r\nfunction fadeout(e) {\r\n    div.transition().duration(400).style(\"opacity\", 0);\r\n}\r\nfunction moviemouse(e) {\r\n    div.style(\"left\", d3.mouse(this)[0])\r\n        .style(\"top\", d3.mouse(this)[1]);\r\n}\r\n\r\nd3.json(\"query.php?room=w004\", makeComputers);<\/pre>\n<p>Just to break this down in summary:<\/p>\n<ul>\n<li>The makeComputers function starts off by adding all of the computers imported to the\u00a0<em>computers<\/em> array based on their hostname<\/li>\n<li>It then adds in rectangles, with the mouse listeners, using the better D3 method of adding SVGs to the page.<\/li>\n<li>I&#8217;ve also used the findIndex function without needing a <em>for<\/em> loop, since D3 provides the\u00a0<em>index\u00a0<\/em>to be able to do this.<\/li>\n<li>There are no longer any computers specified\u00a0in the array of computers<\/li>\n<li>There are now three functions associated with fading in\/out and movement of the mouse (so the position of the tooltip box will change as the mouse moves)<\/li>\n<li>Finally, this all happens when I call <em>makeComputers\u00a0<\/em>at the end as the function that is executed once the request to the given URL has completed<\/li>\n<\/ul>\n<p>The only last modification you may need to make, if your <em>query.php\u00a0<\/em>file is stored on a different server to the one you are running your\u00a0<em>index.html<\/em> and\u00a0<em>main.js<\/em> from, is to add this to the top of your <em>query.php<\/em> file:<\/p>\n<pre class=\"lang:php decode:true\">&lt;?php \r\n header(\"Access-Control-Allow-Origin: *\");<\/pre>\n<p>This allows calls from other domains to be made, which is disabled by default for security reasons. However, if you&#8217;re confident that (for testing purposes at least) this won&#8217;t be an issue, then you can go ahead and enable to it, which lets you do things like run your index.html page and JS files from your local disk and make queries to the remote server.<\/p>\n<p>This should have hopefully got you to a stage where you can make requests for JSON data to a web server and have it return some SQL results as readable data by JavaScript, but if anyone has any issues or encounters any oddities, do get in touch!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Carrying straight on from\u00a0my previous post\u00a0on the subject, I&#8217;m going to go through the alluded to third page to add, which is\u00a0query.php and change\u00a0main.js a little, too. The purpose is to swap out the hard coded arrays of data for an external data source, namely a web-based resource rather than any locally stored files using [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_newsletter_tier_id":0,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[21],"tags":[65,64,62,34,36],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6PQZ3-7b","_links":{"self":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts\/445"}],"collection":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=445"}],"version-history":[{"count":2,"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts\/445\/revisions"}],"predecessor-version":[{"id":448,"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts\/445\/revisions\/448"}],"wp:attachment":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=445"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=445"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=445"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}