How to receive data from a button in a form? - javascript

This seems very simple but I can't figure it out. I am trying to get back the value of a button so I can conditionally render a page template. I can't seem to capture the value of the button though.
Here is my js code.
app.get("/trades", (req, res) => { // line 465
let button_value = req.params.value;
console.log(button_value);
// res.render("activate_pause")
});
And here is my html/ejs markup:
<form action="/trades" methods="get, post" name="groups_button">
<button class="orange" type="submit" name="frontpage_trades_button" value="pause">Pause Campaigns</button>
<button type="submit" name="frontpage_trades_button" value="enable">Activate Campaigns</button>
</form>
The values 'pause' or 'enable' will be used to set a status of true or false, which will render the desired template ( or so I hope!). Can anyone help??

methods is not a valid attribute of the <form> element
console.log('Is method in <form>?: ', 'method' in HTMLFormElement.prototype);
console.log('Is methods in <form>?: ', 'methods' in HTMLFormElement.prototype);
So the first thing to do is to replace methods with method and set only one method (I'd suggest POST).
Then in the Node.js code, make sure you have the proper middleware in order to decode URL encoded forms:
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
This should allow you to retrieve the value of the clicked button, like this:
app.post("/trades", (req, res) => {
let button_value = req.body.frontpage_trades_button;
console.log(button_value);
// res.render("activate_pause")
});

u can use data attribute to take it
<button type="submit"id="x" name="frontpage_trades_button" data-value="enable">Activate Campaigns</button>
app.get("/trades", (req, res) => { // line 465
let button_value = req.params.value;
console.log($("#x").data("value"));
// res.render("activate_pause")
});

Related

Split string when comma is inserted and add it to a string array

I have an input that converts strings into 'styled tags' when the user types a comma, then, when the form is submitted, the strings are pushed into an array called 'content'.
On the EJS views, I am printing the result like <%= course.content %> but the result I am getting is 'string1,string2,string3,string4' and what I am after is that when is pushed into the array, each string must be a different element:
content ['string1','string2','string3','string4']
only then it will render properly in my views by looping the array. I want to achieve this by javaScript or jQuery only, please.
UPDATE: this is how I am rendering in my view
<ul>
<% var i; for (i = 0; i < course.content.length; i++) { %>
<li><i class="fas fa-check"></i> <%= course.content %></li>
<% }; %>
</ul>
UPDATE: this is my route where this POST request is being done
router.post("/", middleware.isLoggedIn, function(req, res) {
Course.create(req.body.course, function(err, course) {
if (err) {
req.flash("error", err.message);
return res.redirect("back");
}
res.redirect("/courses/" + course.id);
});
});
SOLVED! by using split on the server side like this:
router.post("/", middleware.isLoggedIn, function(req, res) {
Course.create(req.body.course, function(err, course) {
if (err) {
req.flash("error", err.message);
return res.redirect("back");
} else {
var content = req.body.course.content.toString().split(",");
course.content = content;
course.save();
console.log(course.content);
res.redirect("/courses/" + course.id);
}
});
});
Here is another solution in javaScript using function beforesubmit() by #garry man see below.
codepen
Long way
Otherwise there's one work around is as many tags you enter, that much input elements you should generate.
For e.g. my input tags are foo,bar then 2 input tags will be generated like
Note square brackets below
<input id="hiddenInput" type="hidden" name="course[content][]" required>
<input id="hiddenInput" type="hidden" name="course[content][]" required>
This is long way.
Another way
If you submit form via AJAX, then you can manipulate data before submitting and convert string into array with the use of .split(',').
Another way (Server side)
Split string by , on server side.
Okay so the problem is that you are submitting a form containing a single input which can only contain string values.
HTML Form practice is to have a single input for each array element to be posted, like:
<input name="course[content]"/> //value 1
<input name="course[content]"/> //value 2
<input name="course[content]"/> //value 3
So, in order to achieve that, before submit, you can call this function that generates those elements for you:
function beforesubmit(){
let submitVal = document.getElementById('hiddenInput');
let values = submitVal.value.split(',');
//let's get the container of the params passed to post
let paramsContainer = submitVal.parentElement;
// remove the param containting the string with commas as we're generating new ones
paramsContainer.removeChild(submitVal);
for(let i =0; i<values.length;i++){
//for each value, add a submit param containing each value
let tmp = document.createElement('input');
tmp.setAttribute('type','hidden');
tmp.setAttribute('name','course[content]');
tmp.setAttribute('value',values[i]);
paramsContainer.appendChild(tmp);
}
document.getElementsByTagName('form')[0].submit();
}
in order to call this function, replace your submit input with this:
<input type="button" value="submit" onclick="beforesubmit()">
Using this code you can already see the POST request difference between before and after. In your codepen it sends a single parameter, while with this snippet of code you are going to send an array of course['content'].
Now it's all up to how you are going retrieve data server side, you should be retrieving the course['content'] param as an array.

button should delete specific item on server side

I'm working on an app to track my expenses with javascript, nodejs , express and as templating engine handelbars.
So I have a div "list" which contains all expenses. ( i have an add button next to the div list, not visible on the pic)
Everytime a I add an expense , I add the div "obj" with a delete button ,a "B" button and some information about the expense.
here is the code in my html:
<div class="list">
{{#each expArr}}
<div id="obj" class="obj">
<form action="/removetodo" method="POST" >
<button class="btn2">X</button>
</form>
<button onclick="openNav()" class="btn">B</button>
<a>{{date}}</a> <n>{{name}}</n> <a>{{value}} </a>
{{description}}
</div>
{{/each}}
Now, my backend is runnning on a NodeJS server with express.
here is my index.js file :
var express = require('express');
var router = express.Router();
var expArr = [];
router.get('/', function(req, res, next) {
res.render('index' , {expArr: expArr} );
});
router.post('/addtodo', function (req, res, next) {
var exp = new Object();
exp.name = req.body.name;
exp.value = req.body.val;
exp.date = req.body.date;
exp.description = req.body.descr;
expArr.push(exp);
res.redirect('/');
});
router.post('/removetodo', function (req, res, next) {
expArr.pop();
res.redirect("/");
});
module.exports = router;
In addtodo I simply adding all the informtion to an array on the server (later I will add a database).
Now my question:
The delete button on every expense should delete the right expense.
In other words, I want ,by clicking the delete button , that the right entry in the array on the server deletes.
How do I do that?
Thanks!
you're storing everything in memory, taking that for granted, you can start by using a plain object rather then an array to store your data
expArr = {}
and then add a unique identifier like a hash or a date in ms for each instance
var exp = new Object();
exp.id = new Date().getUTCMilliseconds();
exp.name = req.body.name;
exp.value = req.body.val;
exp.date = req.body.date;
exp.description = req.body.descr;
expArr[exp.id] = exp;
now be sure to pass from the client the right id when you want to remove an expense
router.post('/removetodo', function (req, res, next) {
if(expArr[req.body.id]) {
delete expArr[req.body.id];
}
res.redirect("/");
});

Making Variable Passed Through Pug Available to Javascript

I have a Pug view that gives a set of links to a user. Before you get to the page the user has already authenticated and I have the username and their department in session variables. I can pass them as variables to the view like this:
res.render('landingpage', { title: 'Landing Page',
username: req.session.username,
department: req.session.department });
And then in the view I have this line and it works:
p(class='navbar-text') Welcome #{username} from #{department}
which prints "Welcome Bob from Accounting" at the top with no problem.
But what I need to do is control whether some of the links are visible based upon the passed in department. (The department was discovered in the authentication function that passed the user onto the landing page and placed into the session.)
I was trying to place this into the document ready function but that doesn't work as it is undefined. What I need to do is to be able to change the visibility attribute and the onclick event for a link based upon the department. I have a JSON configuration file that tells me the departments allowed to access the link but I can't figure out how to translate that department variable into a javascript function that I can call to change the visibility.
I've tried to add it to a document ready function as department and #{department} but it just ends up either not knowing what it is or using it like the literal string. Any ideas of how to proceed?
Okay so I didn't post enough information for anyone to see what I was asking. Sorry. In the process of creating a new post and cutting it all down to just the part I needed I got it to work. This code works:
In the app.js file
'use strict';
var express = require('express');
var app = express();
app.set('views', './views');
app.set('view engine', 'pug');
var session = require('express-session');
var FileStore = require('session-file-store')(session);
var fileStoreOptions = {
path: './sessions',
ttl: 86400
};
var sessionOptions = {
secret: 'SecretKey',
resave: false,
saveUninitialized: false,
name: 'sessionId',
store: new FileStore(fileStoreOptions)
};
app.use(session(sessionOptions));
app.get('/landingpage', function(req,res,next) {
req.session.accessToken = true;
req.session.username = 'Bob';
req.session.department = 'Accounting';
res.render('landingpage', { title: 'Landing Page',
username: req.session.username,
department: req.session.department });
});
app.get('/images/DarkBlueIcon.png', function(req,res) {
res.sendFile(__dirname + '/images/DarkBlueIcon.png');
});
app.get('/node_modules/jquery/dist/jquery.min.js', function(req,res) {
res.sendFile(__dirname + '/node_modules/jquery/dist/jquery.min.js');
});
var server = app.listen(3000, function () { });
and this is in the pug file:
doctype html
html
head
title=title
script(src='/node_modules/jquery/dist/jquery.min.js')
body
p(class='navbar-text') Welcome #{username} from #{department}
img(id='accessApproved' src='/images/DarkBlueIcon.png' class='overlay' style='visibility: hidden;')
script.
$(document).ready( function() {
if('#{department}' === 'Accounting') {
document.getElementById('accessApproved').style.visibility = 'visible';
}
});
Gives you this:
screenshot of Hello to Bob and Blue Icon
To clarify from the other solutions: the interpolated string must be nested within quotation marks. If you don't use the quotation marks around the #{} then javascript tries to read it as a variable.
Solution:
'#{department}' === 'Accounting'
Explanation:
The solution evaluates to 'Accounting' === 'Accounting',which is true. The incorrect way would be to forget the quotes, and try #{department} === 'Accounting' which evaluates to Accounting === 'Accounting', which is the same as Undefined === 'Accounting', which is false.
You can use a hidden input to pass the department info and get the input's value in js.
Example:
doctype html
html
head
title=title
script(src='/node_modules/jquery/dist/jquery.min.js')
body
input#department(type='hidden',value=department)
p(class='navbar-text') Welcome #{username} from #{department}
img(id='accessApproved' src='/images/DarkBlueIcon.png' class='overlay' style='visibility: hidden;')
script.
$(document).ready( function() {
var department = $('#department').val();
if(department === 'Accounting') {
document.getElementById('accessApproved').style.visibility = 'visible';
}
});

ReCaptcha validation on Node.js

on my express server, g-recaptcha-response always return nothing. This is my code at the moment:
<div class=container-fluid id=mcenter>
<form id='monitstart' action="/monitstart" method="POST">
<input type="text" class="form-control" aria-label="..." name=>
<button class="g-recaptcha" data-sitekey="SITEKEYHERE" data-callback="onSubmit"></button>
</form>
</div>
and on my server
var express = require('express')
var router = express.Router()
var request = require('request')
router.post('/monitstart', function (req, res) {
request({url: "https://www.google.com/recaptcha/api/siteverify?secret=SECRETHERE&response=" + req.body['g-recaptcha-response']} + '&remoteip=' + req.connection.remoteAddress, function (err, response, body) {
req.send(body)
})
})
module.exports = router
And when I try to validate it, it give me a code 500 and say TypeError: Cannot read property 'g-recaptcha-response' of undefined
The correct answer, accepted by the OP during the short discussion below the question involves adding the body parsing middleware (the body-parser) to the pipeline so that the body property of the request object is correctly initialized before a value is read from there.

Uploading file from form to server

I am currently working on a dashboard, and have been stuck for some couple of hours now... What I like to do is to have a form including 3 <input type="file"> (two of them allow multiple files, one not), and each of them posting to 3 different post-methods in the server. When I try to console log the request on the server side the data is empty ({}) . I do not understand why, can someone please help me solve this issue?
I am using angularjs, and nodejs btw.
This is what my current code is : (files and paths are dummy-names)
HTML:
<form role="form" enctype="multipart/form-data">
<div class="form-group">
<label for="file1">File1:</label>
<input type="file" id="file1" accept=".txt, .json" multiple>
</div>
<div class="form-group">
<label for="file2">File2:</label>
<input type="file" id="file2" accept=".json">
</div>
<div class="form-group">
<label for="file3">File3:</label>
<input type="file" id="file3" multiple>
</div>
<button type="button" ng-click="save()" class="btn btn-default"> Save</button>
</form>
JS:
module.exports = function($scope, $http) {
$scope.save = function () {
file1(document.getElementById('file1').files);
file2(document.getElementById('file2').files);
file3(document.getElementById('file3').files);
};
function file1(files) {
$http.post('/file1', {f: files}).success(function (res) {
//todo
});
};
function file2(files) {
$http.post('/file2', {f: files}).success(function (res) {
//todo
});
};
function file3(files) {
$http.post('/file3', {f: files}).success(function (res) {
//todo
});
};
}
Server.js
var express = require("express"),
fs = require("fs"),
bodyParser = require('body-parser'),
app.use(express.static("build"));
app.use(bodyParser());
app.post('/file1', function (req, res) {
console.log(req.body.f) // returns empty: {}
// like to move files to path: a/b/c
});
app.post('/file2', function (req, res) {
// like to move files to path: a/d/e
});
app.post('/file3', function (req, res) {
// like to move files to path: a/f/g
});
Update:
after receiving the answer from GrimurD, I have modified the server.js, but still struggling. Any takers?
var express = require("express"),
fs = require("fs"),
bodyParser = require('body-parser'),
multer = require('multer'), //<-- updated
upload = multer({ dest: './uploads/' }) //<-- updated
app.use(express.static("build"));
app.use(bodyParser());
app.use(multer({ dest: './uploads/' }).array()); // <-- updated
app.post('/file1', upload.array('file1'), function (req, res) {
console.log(req.body.f) // returns empty: {}
console.log(req.files); // returns undefined // <-- updated
// like to move files to path: a/b/c
});
app.post('/file2', upload.single('file2'), function (req, res) {
console.log(req.file); // returns undefined // <-- updated
// like to move files to path: a/d/e
});
app.post('/file3', upload.array('file3'), function (req, res) {
console.log(req.files); // returns undefined // <-- updated
// like to move files to path: a/f/g
});
When uploading a file the form must use multipart/form-data which body-parser doesn't support. You must use a specialized middleware to handle this type. Multer is one such that I have used with success.
You can get around this using FileReader.readAsDataURL(..) on the File objects in the input fields. I often like to use the multi-select ability on the file input type so that I can upload a bunch of files and do this async.
So what I normally do is access the files property on the input element and loop through them, then I use the FileReader.readAsDataURL to get the base64 of the binary for the files then passing the base64 to a webserivce method with a signature that accepts that a string param for the base64, convert the b64 to binary and you're back in business.
var fileRunner = {
files: [],
run: function(el) {
this.files = el.files;
this.read();
},
read: function() {
// begin the read operation
console.log(this.files.length);
for (var i = 0; i <= this.files.length - 1; i++) {
var reader = new FileReader();
reader.readAsDataURL(this.files[i]);
reader.onload = this.process
}
},
process: function(evt) {
var result = evt.target.result
if (result.length > 0) {
var parts = result.split(',')
var dataBsf = parts[parts.length - 1];
console.log(dataBsf);
//call method to pass the base 64 here.
}
}
};
<body>
<input id="fup" type="file" multiple="multiple" onchange="fileRunner.run(this)" />
</body>
I did not include a server side component to this because I think that should be agnostic and slightly out of scope here.
I am only logging the output to the console, but you would take the output and pump it to the web service.
Additionally, I referenced the input element in the event handler for the onchange using "this" because I had no idea how you wanted to handle it. Allowing the passing of an element provided a bit of loose assumptions on my part.

Categories