Node.js get textbox value that corresponds to checkbox

I want to get the value of the textboxes that correspond to checked checkboxes for each item in my SQL table. Below, I have a html table of all the items in my SQL table and I have some javascript code that is supposed to return the values of the textboxes that correspond to checked checkboxes. Am I doing it correctly and how do I get the return value of the javascript function in Node.js?

<form method="POST" action="/topic7/mid-term/calculate">
        <table style="width:100%">
                <tr>
                        <th>Selected</th>
                        <th>Amount</th>
                        <th>Name</th>
                        <th>Typical values</th>
                        <th>Unit of typical values</th>
                        <th>Calories</th>
                        <th>Carbs</th>
                        <th>Fat</th>
                        <th>Protein</th>
                        <th>Salt</th>  
                        <th>Sugar</th>
                </tr>
 
                <% availableFood.forEach(function(food_item){ %>
                <tr>
                        <td><input type="checkbox" name="checkbox[]" value= "<%= food_item.name %>"></td> 
                        <td><input type="text" name="amt" value= "1" style="width: 30px;"></td>
                        <td><%= food_item.name %></td>
                        <td><%= food_item.typical_values %></td> 
                        <td><%= food_item.unit_of_the_typical_value %></td>
                        <td><%= food_item.calories %></td>
                        <td><%= food_item.carbs %></td>
                        <td><%= food_item.fat %></td>
                        <td><%= food_item.protein %></td> 
                        <td><%= food_item.salt %></td> 
                        <td><%= food_item.sugar %></td>
                </tr>
                <% }) %>                     
        </table>
        <p><input type="submit" value="Calculate sum" onclick='getAmount("checkbox","amt");'/></p>
        <script>
                function getAmount() {
                        var checkbox = document.getElementsByName('checkbox');
                        var amount = document.getElementsByName('amt');
                        var amt = '';
                        for(var i = 0; i < checkbox.length; i++) {
                                if(checkbox[i].checked == true) {
                                        amt += amount[i].value + ',';
                                }
                        }
                        return amt;
                }
        </script>
</form>

Well for starters you’re not preventing your form from submitting, so it doesnt really matter what your javascript does. But that’s besides the point, I suppose.

What is your script returning amt to? Right now it returns it to the handler of the onclick event, which is going to shrug at receiving a string and proceed with its normal ‘you clicked a submit button’ logic.

I want amt to return to “/topic7/mid-term/calculate” route in my node.js file but I don’t know how to do that. Also, how do I stop the form from submitting?

To stop the form submitting you need to use event.preventDefault()

You can pass the event object from you onclick statement to getAmount

Worth a mention, as with php you can use Array’s higher order functions. For example in JS forEach, filter, map, reduce etc.

An alternative based on your code with forEach

let figures = []

checkboxes.forEach(
  function(checkbox, i) {
    if (checkbox.checked === true) {
      figures.push(amounts[i].value)
    }
  }
)
// Array.join() e.g. [1,2,3].join(',') -> string '1,2,3'
console.log(figures.join(','))

Thank you. For your alternative method, the code is placed within my getAmount() function right? Do I need to set the value for checkboxes as

var checkboxes = document.getElementsByName(‘checkbox’);

From my Node.js file, do I just use figures.join(’,’) to get the values?

Well if we’re going to start talking about higher order functions… your entire function can be reduced to a (admittedly fairly long) one-liner…

return [...document.querySelectorAll('input[type="checkbox"]:checked')].map((x) => x.parentElement.nextElementSibling.children[0].value).join(",");

How do I access that return value in my node.js route?
Also is the full line just like this or I have to add something in the ... part?

return [...document.querySelectorAll('input[type="checkbox"]:checked')].map((x) => x.parentElement.nextElementSibling.children[0].value).join(",");

Is there an issue m_hutley? or am I misreading that?

Seeing the link 'topic7/mid-term/calculate’ link I presumed this was coursework, so gave a few higher order function links from MDN that he could consider.

The forEach ties in with the php he is already using above, so thought it was an apt example.

Personally I would imagine using something like reduce.

No, no issue, just saying that if we’re going higher order, might as well condense too :slight_smile:

Typically, if you are trying to transmit data from the form to the designate route, you would not suppress the form acting, and would instead output the value into a hidden input field to be picked up by the code on the other end.

In this case however, the data is already in the form, so… I would just… have the receiving page do the calculation there instead?

1 Like

Can I just req.body.amt the textbox value to get the return value of the javascript function?

I’ve been a bit stupid here, that’s ejs not PHP isn’t it? Mixing my ? and %

I believe the simplest route would be to use ‘express’

This is grabbed from stackoverflow

html

<form method="post" action="/">
    <input type="text" name="user[name]">
    <input type="text" name="user[email]">
    <input type="submit" value="Submit">
</form>

app.js

// Parse URL-encoded bodies (as sent by HTML forms)
app.use(express.urlencoded());

// Parse JSON bodies (as sent by API clients)
app.use(express.json());

// Access the parse results as request.body
app.post('/', function(request, response){
    console.log(request.body.user.name);
    console.log(request.body.user.email);
});

Thanks but I’ve tried request body in Node.js and it didn’t give me the return value from the javascript function. Unless there’s something wrong with the javascript. Here’s what I ended up using:

<script>
        function getAmount() {
                const checkboxes = document.querySelectorAll('input[name="checkbox"]:checked');
                var amount = document.getElementsByName('amt');
                let values = []
                checkboxes.forEach((checkbox) => {
                        values.push(amount.value);
                });
                return values.join(",");
        }
</script>

Or I might not be requesting the correct thing. I’ve tried asking on stackoverflow and nobody seems to know how to get the javascript function results in the Node.js route.

ahmadalibin,

Someone else, maybe able to comment, but I am having a look at it.

I did start a nodejs course myself, but it ended up on the backburner some months ago. Doing a bit of a refresh

This is the npm link for express.

Generally npmjs.com is the place to look for up to the date instructions.

A more detailed link on installation

Also for previewing/testing

Thanks for your help.

@ahmadalibin This very much cobbled together and just a start.

Express is now in version 4.x, so the stuff I learnt in 3.x throws errors now. The documentation I think is pretty bad. A classic example of very clever programmers, but terrible teachers.

In real world, you would more than likely use an MVC pattern, so data, routes, controllers, views would all be in separate folders.

Following edited

package.json

{
  "name": "node-form",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ejs": "^3.1.5",
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  }
}

views folder (Note: head, footer etc. would be best put in an includes folder)

form.ejs

<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta charset='UTF-8' />
    <meta name='viewport' content='width=device-width, initial-scale=1.0' />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <form method='POST' action='/'>
      <table>
        <tr>
          <th>Selected</th>
          <th>Amount</th>
          <th>Name</th>
        </tr>
        <% foodItems.forEach((foodItem) => { %>
        <tr>
          <td>
            <input
              type='checkbox'
              name='checked[]'
              value='<%= foodItem.id %>'
            />
          </td>
          <td>
            <input type='text' name='amounts[<%= foodItem.id %>]' value='<%= foodItem.value %>' />
          </td>
          <td><%= foodItem.name %></td>
        </tr>
        <% }) %>
      </table>
      <p>
        <button type='submit'>Calculate Sum</button>
      </p>
    </form>
  </body>
</html>

app.js

// added an id for each item
const foodItems = [
  {
    id: 'banana',
    name: 'Banana',
    value: 89
  },
  {
    id: 'mars-bar',
    name: 'Mars bar',
    value: 230
  },
  {
    id: 'milkshake',
    name: 'Milkshake',
    value: 112
  }
]

const express = require('express')
const app = express()
const ejs = require('ejs')
const port = 3000

app.set('view engine', 'ejs')
// urlencoded needs to be set to true
app.use(express.urlencoded({ extended: true }))
app.use(express.json())

app.get('/', (req, res) => {
  res.render('form', {
    foodItems,
    pageTitle: 'Food Form'
  })
})

app.post('/', (req, res) => {
  const { amounts, checked } = req.body

  // check whether we have any checked entries first
  if (Array.isArray(checked)) {
    const selectedAmounts = checked.map(idName => amounts[idName])

    console.dir(req.body)
    console.log(selectedAmounts)
  }
  res.redirect('/')
})

app.listen(port)

If we go to localhost:3000, select say the banana and the milkshake and click on Calculate Sum the output in the terminal is

{
  checked: [ 'banana', 'milkshake' ],
  amounts: { banana: '89', 'mars-bar': '230', milkshake: '112' }
}
[ '89', '112' ]

I guess albeit flawed, this is a start

Wow thanks so much for your time. My coursework requires me to let users select some food items (e.g. by displaying a checkbox next to each food item and letting the user input the amount of each food item in the recipe e.g. 2x100 g flour) and calculating the sum of nutritional information. Currently if I just send using POST, my code returns the amount for all food items in my table instead of just the ones that have the checkbox checked which is the reason for trying to use the javascript function to create a new array of the amount values that have their checkbox checked.

@ahmadalibin

I edited the above code, while you typed this message. Hopefully getting a bit closer.

For this part const { amounts, checked } = req.body, does it automatically get the values of the inputs with the same name from the html?

It’s destructuring. If you see the req.body output

{
  checked: [ 'banana', 'milkshake' ],
  amounts: { banana: '89', 'mars-bar': '230', milkshake: '112' }
}

This

const { amounts, checked } = req.body

Is shortform for

const amounts = req.body.amounts
const checked = req.body.checked

yes the same names as the html

name='checked[]'
name='amounts[<%= foodItem.id %>]'

Thanks very much, I managed to get your code to work in my node.js.

1 Like