Node + Express + Swig / pass local json file to template

1k views Asked by At

I am a node noob, I am able to get express / swig to play nice, and display on screen, when I introduce my d3 code (which works by itself) the console comes back with Uncaught TypeError: Cannot read property 'forEach' of undefined(anonymous function) @ json:63event @ d3.js:504respond @ d3.js:1942

Things to note - the data.json is in the same directory as templates.

Am I going about this the wrong way? Would it be better to do my d3 rendering inside node? If so, how do I pass that to a swig template etc?

Edit - I noticed in the dev tools the json wasn't being retrieved, the but SVG renders. I commented out the following since express was hijacking the get.

// app.get('/*', function (req, res) {
//  res.send("404 bro");
// });

Now a new error has risen, which is a good sign!

GET http://localhost:3000/templates/data.json 404 (Not Found)

Again, sorry I am somewhat of a node nooby.

Update - here is my updated code

var data = require('./templates/data/data.json');
console.log("====== Loaded Jason ======" +  JSON.stringify(data));

app.get('/json', function(req, res){
    res.render('line', {'title' : 'First Swig Template'});
    res.sendFile(data);
});

It comes back with

Unhandled rejection TypeError: undefined is not a function

Update - here is my updated code

app.get('/json', function(req, res){
    res.render('line', {'title' : 'First Swig Template'});
    res.sendFile('data.json', { root: path.join(__dirname, '/templates/data') });
});

Which comes back with

Error: Can't set headers after they are sent.

The Json is now displaying on the screen though.

Node code

// Retrieve
var MongoClient = require('mongodb').MongoClient;
//Swig for template
var swig = require('swig');
//consolidate -templating consolidation library
var cons = require("consolidate");
// Express
var express = require('express');
var app = express();

app.engine('html', cons.swig);
app.set('view engine', 'html');
app.set('views', __dirname + '/templates');

app.get('/swig', function(req, res){
    res.render('home', {'title' : 'First Swig Template'});
});
app.get('/json', function(req, res){
    res.render('line', {'title' : 'First Swig Template'});
});
//setup routes
app.get('/*', function (req, res) {
    res.send("404 bro");
});
//setup server
var server = app.listen(3000, function () {

  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);

});


// Connect to the db
MongoClient.connect("mongodb://localhost:27017/linejson", function(err, db) {
  if(!err) {
    console.log("We are connected");
  }

  var collection = db.collection('dummy');

 // count records
    collection.count(function(err, count) {
          console.log("There are " + count + " records.");
        });
    collection.find().each(function(err, doc) {
          if(doc != null) console.log("Doc from Each ");
          console.dir(doc);
        });
});

Html template + D3 code

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Data Visualization with D3 </title>
        <!-- Bootstrap CDN CSS -->
        <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
        <style> /* set the CSS */
        body { font: 12px Arial;}
        path {
        stroke: steelblue;
        stroke-width: 2;
        fill: none;
        }
        .axis path,
        .axis line {
        fill: none;
        stroke: grey;
        stroke-width: 1;
        shape-rendering: crispEdges;
        }
        </style>
    </head>
    <body>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.js"></script>
        <script>
        // Set the dimensions of the canvas / graph
        var margin = {top: 30, right: 20, bottom: 30, left: 50},
        width = 600 - margin.left - margin.right,
        height = 270 - margin.top - margin.bottom;
        // Parse the date / time
        var parseDate = d3.time.format("%d-%b-%y").parse;
        // Set the ranges
        var x = d3.time.scale().range([0, width]);
        var y = d3.scale.linear().range([height, 0]);
        // Define the axes
        var xAxis = d3.svg.axis().scale(x)
        .orient("bottom").ticks(5);
        var yAxis = d3.svg.axis().scale(y)
        .orient("left").ticks(5);
        // Define the line
        var valueline = d3.svg.line()
        .x(function(d) { return x(d.date); })
        .y(function(d) { return y(d.close); });

        // Adds the svg canvas
        var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");
        // Get the data
        // d3.json("data.json", function(data) {
        // data.forEach(function(d) {
        // d.date = parseDate(d.date);
        // d.close = +d.close;
        // });
        d3.json("data.json", function(error, data) {
        data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
        });
        // Scale the range of the data
        x.domain(d3.extent(data, function(d) { return d.date; }));
        y.domain([0, d3.max(data, function(d) { return d.close; })]);
        // Add the valueline path.
        svg.append("path")
        .attr("class", "line")
        .attr("d", valueline(data));
        // Add the X Axis
        svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
        // Add the Y Axis
        svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);
        });
        </script>
    </body>
</html>
2

There are 2 answers

0
AudioBubble On BEST ANSWER
app.use(express.static(__dirname + '/data'));

The following line fixed it! Thanks for your inputs!

0
Alex_B On

Since you use node to handle your interactions with the server you have provide a path to the file through node. similar to the following.

app.get('/templates/data', function(req, res){
    res.sendfile(data.json);
});

And then put the file in the /template/data/data.json path.

Hope this helps.