Search code examples
javascriptnode.jsexpressaxioshandlebars.js

How do I make hbs render an array from a callback function?


Currently I have axios and cheerio return data from a webpage. I then setup express to setup a few views. I double checked my index.hbs and it include {{data}} inside the body. This should allow the page to render the text from the index render data: dealss . Am I missing anything ? The dealss obj holds 4 different objects that I can access.

getdeals(result => console.log(result.totaldeals[0].date))

This returns [ 09/04/2019/ ] in the console.

const path = require('path')
const express = require('express')
const hbs = require('hbs')
const axios = require('axios');
const cheerio = require('cheerio');

const app = express()

// Define paths for express config
const publicDirPath = path.join(__dirname, '../public')
const viewsPath = path.join(__dirname, '../templates/views')
const partialsPath = path.join(__dirname, '../templates/partials')

// Setup handlebars engine and views location
app.set('view engine', 'hbs')
app.set('views', viewsPath)
hbs.registerPartials(partialsPath)

// Setup static directory to serve
app.use(express.static(publicDirPath))


// Views
app.get('', (req, res) => {
    res.render('index', {
        title: 'ClearVision',
        data: dealss,
        name: 'Chris'
    })
})

app.get('/about', (req, res) => {
    res.render('about', {
        title: 'ClearVision - About Us',
        header: 'About Us',
        name: 'Chris'
    })
})

app.get('/help', (req, res) => {
    res.render('help', {
        title: 'ClearVision - Help',
        helptext: 'Please contact x for help.',
        name: 'Chris'
    })
})

app.get('/weather', (req, res) => {
    res.send({
        forecast: 'It is sunny.',
        location: 'x, Ca'
    })
})

app.listen(1337, () => {
    console.log('Server is currently running on port 1337.')
})

const url = 'https://abcdef.com/';
axios.defaults.withCredentials = true


// Get the deals
const getdeals = (callback) => {
    axios(url, {
            headers: {
                Cookie: "x=xx;"
            }
        })
        .then(response => {
            const html = response.data;
            const $ = cheerio.load(html);

            // Deals Page
            const statsTable = $('tbody > tr');
            const totaldeals = [];


            // Loop Table for data in each row
            statsTable.each(function () {
                const nwline = "\n"
                let date = $(this).find('td:nth-child(1)').text()
                let bodydeals = $(this).find('td:nth-child(2)').text()
                let newdeal = $(this).find('td:nth-child(3)').text()
                let revdeal = $(this).find('td:nth-child(4)').text()
                let monthlydealrev = $(this).find('td:nth-child(5)').text()

                // Clear /n
                if (date.includes(nwline)) {
                    date = date.toString().replace("\n", ""),
                        date = date.toString().replace("\n", "")
                }

                // Clear /n
                if (bodydeals.includes(nwline)) {
                    bodydeals = bodydeals.toString().replace("\n", ""),
                        bodydeals = bodydeals.toString().replace("\n", ""),
                        bodydeals = bodydeals.toString().replace("\n", "")
                }

                // Clear /n
                if (newdeal.includes(nwline)) {
                    newdeal = newdeal.toString().replace("\n", ""),
                        newdeal = newdeal.toString().replace("\n", ""),
                        newdeal = newdeal.toString().replace("\n", "")
                }

                // Clear /n
                if (revdeal.includes(nwline)) {
                    revdeal = revdeal.toString().replace("\n", ""),
                        revdeal = revdeal.toString().replace("\n", ""),
                        revdeal = revdeal.toString().replace("\n", "")
                }

                // Clear /n (lookup jquery table functions)
                if (monthlydealrev.includes(nwline)) {
                    monthlydealrev = monthlydealrev.toString().replace("\n", ""),
                        monthlydealrev = monthlydealrev.toString().replace("\n", ""),
                        monthlydealrev = monthlydealrev.toString().replace("\n", "")
                }

                totaldeals.push({
                    date,
                    bodydeals,
                    newdeal,
                    revdeal,
                    monthlydealrev
                })



            })
            callback({
                totaldeals
            })
            //console.log(totaldeals[1].date)
        })
        .catch(console.error);

}

function newFunction() {[getdeals(result => console.log(result.totaldeals))]}

I added a data: dealss under the res.render for the index. I also checked the index.hbs which has {{data}} in there. Shouldn't this just add the text to the screen?

Any ideas on how to print it to the view?


Solution

  • You just need to pass it as a variable to your hbs file:

    app.get('', (req, res) => {
        getdeals(result => {
            res.render('index', {
                title: 'ClearVision',
                data: result, // or result.totaldeals depending
                name: 'Chris' // on what you really mean
            })
        });
    })
    

    Improvements

    If you rewrite getdeals() to return a Promise instead of accepting a callback you can use async/await:

    const getdeals = () => {
        // NOTE THIS CHANGE, return axios promise:
        return axios(url, {
                /* ... */
            })
            .then(response => {
                /* .. */
                statsTable.each(function () {
                    /* .. */
                })
    
                return totaldeals; // NOTE we return the result instead
                                   // of calling a callback. This will
                                   // return a resolved Promise
            })
            // Don't catch here, your request will hang if an error occurs
    }
    

    Now with the change above (return axios and return the result) we can rewrite the route as:

    app.get('', async (req, res, next) => { // must have async keyword!
        try {
            let result = await getdeals();
    
            res.render('index', {
                title: 'ClearVision',
                data: result, // or result.totaldeals depending
                name: 'Chris' // on what you really mean
            })
        }
        catch (err) {
            console.log(err);
            next(err); // this will close the request socket
        }
    })