SailsJs - Refresh View with new data - node.js

this is my first Node application and I am trying to refresh the view based on filters applied by users. I have a view to show data in table as below
The code for the above is
async function getAll(req, res){
try {
if (req.session.loginError == true) {
return res.redirect('/');
}
//** logic to fetch data in sData[]
return res.view('pages/list', {data:sData});
}
catch(error){
sails.log.error('Controller#getAll :: error :', error);
return res.badRequest('Cannot fetch data at this time.' + error.stack);
}
}
The above works fine. Issue is if we select some date filters like fromDate & toDate, the data from backend is fetched correctly but the view isn't refreshed.
I have put below code in ejs page to send filters to controller:
function search()
{
var searchData = {
fromDate : document.querySelector('#fromDate').value,
toDate : document.querySelector('#toDate').value,
status : document.querySelector('#status').value
};
var xmlhttp=new XMLHttpRequest();
url = "/admin/controller/filterData";
xmlhttp.open("POST",url,true);
xmlhttp.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
xmlhttp.send(JSON.stringify(searchData))
}
Created a new method in controller named filterData as below
async function filterData(req, res) {
try {
if (req.session.loginError == true) {
return res.redirect('/');
}
var sDate = Date.parse(req.param('fromDate'));
var eDate = Date.parse(req.param('toDate'));
var sta = req.param('status')
var data= await ****.find().where({ and: [{ start_date: { '>': sDate, '<': eDate } }, { status: sta }] }).populate("***").sort('createdAt DESC');
let sData = [];
_.forEach(d, function(data){
//logic to format data
sData.push(d);
});
return res.view('pages/list', {data:sData});
}
catch (error) {
sails.log.error('Controller#getfilterData :: error : ', error);
return res.redirect('/admin/***/getAll');
}
}
The sData[] gets filtered records but the page isn't getting refreshed with new data. I am not sure if we can refresh the view like this or need to send filtered data back to function in view and then have to refresh the table from there. Please advise.

You are sending XMLHttpRequest which is an ajax call. So the page won't reload at all. It's better to return a json response from server for /admin/controller/filterData request and then populating the UI with ajax response.

Related

'ERR_HTTP_HEADERS_SENT' after POST request in Node.js/ Express app with request http client

The line res.send("Successfully saved the new address."); throws the
'ERR_HTTP_HEADERS_SENT' error.
I read through other posts concerning the same error and tried return res.send("Successfully saved the new address."), but that doesn't fix it. Any insights?
Note: I am new to this.
Please be kind.
Thanks.
My Code
app.post("/", function(req, res) {
const url = "http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates?";
const street = req.body.street;
const city = req.body.city;
const state = req.body.state;
const zip = req.body.zip;
const yourAddress = "Address=" + street.replace(" ", "+") + "&City=" + city.replace(" ", "+") + "&Zip=" + zip;
const parameters = "&category=&outFields=*&forStorage=false&f=json";
request(url + yourAddress + parameters, function(error, response, body) {
const data = JSON.parse(body);
const newAddress = data.candidates[0].address;
const longitude = data.candidates[0].location.x;
const latitude = data.candidates[0].location.y;
const address = new Address({
address: newAddress,
latitude: latitude,
longitude: longitude
});
address.save(function(err) {
if (!err) {
res.send("Successfully saved the new address.");
} else {
res.send(err);
}
});
});
res.redirect("/");
});
You are doing both res.send() and res.redirect() in the same request handler. You can't send two responses to the same request. That's what generates the warning message you see.
Pick one of the other. You either want to send an appropriate status about the .save() or you want to redirect. One or the other, not both.
In this particular code, the res.redirect() happens first so it's what the client sees. Then, after that and when the request() and the address.save() have both completed, then you try to res.send(), but that is blocked because a response has already been sent and the http connection is already done.
If you just want to redirect upon successful save, you can remove the existing res.redirect() and change to this:
address.save(function(err) {
if (!err) {
res.redirect("/");
} else {
console.log(err);
res.sendStatus(500);
}
I would Agree With #jfriend00 answer , since there can be only one response for the request .
Here's what you can do:
address.save(function(err) {
if (!err) {
res.redirect('/success')
} else {
res.send(err);
}
});
//add a new route
app.get('/success',(req,res)=>{res.send('Successfully saved the new address.')})
And then you can redirect to the home page.
This is just a workaround in this case and can differ logically in each case depending on the requirement
The Other Way is to redirect on the client side itself
eg:If you're on jquery
$.ajax({
statusCode: {
200: function() {
$(location).attr('href', 'to the homepage')
}
}
});

How To Bind Node-js DB Query to Web Form

I'm using node and postgres, I'm new to writing async function, what I'm trying to do is a very simple query that will do a total count of records in the database, add one to it and return the result. The result will be visible before the DOM is generated. I don't know how to do this, since async function doesn't return value to callers (also probably I still have the synchronous mindset). Here's the function:
function generateRTA(callback){
var current_year = new Date().getFullYear();
const qry = `SELECT COUNT(date_part('year', updated_on))
FROM recruitment_process
WHERE date_part('year', updated_on) = $1;`
const value = [current_year]
pool.query(qry, value, (err, res) => {
if (err) {
console.log(err.stack)
} else {
var count = parseInt(res.rows[0].count) + 1
var rta_no = String(current_year) + '-' + count
callback(null, rta_no)
}
})
}
For the front-end I'm using pug with simple HTML form.
const rta_no = generateRTA(function (err, res){
if(err){
console.log(err)
}
else{
console.log(res)
}
})
app.get('/new_application', function(req, res){
res.render('new_application', {rta_number: rta_no})
});
I can see the rta_no in console.log but how do I pass it back to the DOM when the value is ready?
Based on the ajax call async response, it will update the div id "div1" when it gets the response from the Node js .
app.js
app.get("/webform", (req, res) => {
res.render("webform", {
title: "Render Web Form"
});
});
app.get("/new_application", (req, res) => {
// Connect to database.
var connection = getMySQLConnection();
connection.connect();
// Do the query to get data.
connection.query('SELECT count(1) as cnt FROM test ', function(err, rows, fields) {
var person;
if (err) {
res.status(500).json({"status_code": 500,"status_message": "internal server error"});
} else {
// Check if the result is found or not
if(rows.length==1) {
res.status(200).json({"count": rows[0].cnt});
} else {
// render not found page
res.status(404).json({"status_code":404, "status_message": "Not found"});
}
}
});
// Close connection
connection.end();
});
webform.pug - Via asynchronous call
html
head
script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js')
script.
$(document).ready(function(){
$.ajax({url: "/new_application", success: function(result){
$("#div1").html(result.count);
}});
});
body
div
Total count goes here :
#div1
value loading ...
That seems okay, I'm just not sure of this:
The result will be visible before the DOM is generated
This constraint defeats the purpose of async, as your DOM should wait for the returned value to be there. Instead of waiting for it you could just render the page and once the function returns and runs your callback update the value.
Also, perhaps it's worth having a look into promises

Mongoose using $push on an array field overwrites last entry

I am trying to create an eventlog type of field on mongodb records where I can store a list of activity. The first time I run the function, it appends to the array correctly but subsequent calls overwrite the last entry instead of appending. If I restart the server or refresh the page in the browser, it will append once again then repeat the same behavior.
I'm learning node and javascript so I'm sure it's some mistake I've made but I don't seem able to figure it out.
Javascript on the client is a tabulator event.
cellEdited:function(cell){
//cell - cell component
const oldValue = cell.cell.oldValue;
const newValue = cell.cell.value;
const title = cell.cell.column.definition.title;
var report = cell.cell.row.data;
report.event = `Updated ${title} from ${oldValue} to ${newValue}`;
$.ajax({
type: 'POST',
url: '/api/update',
data: report,
dataType: 'json'
});
}
The route that its calling on the server:
app.post('/api/update', isAuthenticated, function(req, res) {
var report = req.body;
var reason = '';
if (typeof report.event !== 'undefined') {
reason = report.event;
delete report.event;
} else {
reason = 'Report updated';
}
db.DamageReport.findOneAndUpdate({ _id: report._id}, report, function (err, doc) {
if (err) {
console.log('Err updating report ', err);
return res.send(500, { error: err});
}
/*
* Write eventlog
*/
var event = {"date": new Date(), "user": req.user.email, "event": reason };
appendLog(doc._id, event);
return res.json(doc);
});
});
The appendLog function:
function appendLog(id, entry) {
/*
* entry format:
* date: Date
* user: String
* event: String
*/
if (typeof(entry.date) !== 'object') {
entry.date = new Date();
}
db.DamageReport.findByIdAndUpdate(id, {$push: {eventLog: entry}}, function(err, result) {
if (err) {
return console.log('Error writing eventLog: ', err);
}
return(result);
});
}
It wouldn't append more than one because the previous save contained the Eventlog array in it's original form so every time it saved, it set it back to the original array and then appended the last update.

Node JS mongoose Query is not producing result

Been stuck on this problem for hours. The below code:
router.route("/contact")
.get(function(req,res){
var response = {};
Contact.find({},function(err,data){
if(err) {
response = {"error" : "Error fetching data"};
} else {
response = {"message" : data};
}
res.json(response);
});
})
Above query produces result with all the contacts in the database but
router.route("/contact/department/:dept")
.get(function(req,res){
var response = {};
var arrDept = req.params.dept.split(",");
if(arrDept.length == 0){
response = {"error": " Please enter Department keywords"};
}
else{
response = {};
arrDept.forEach(function(currentValue){
Video.find({dept: '/'+currentValue+'/i'}, function(err, data){
if(err){
response[currentValue] = "No data found";
}else{
response[currentValue] = data;
}
});
});
}
res.json(response);
});
this code does not produce any output.
The code is entering the forEach loop in the else block but the query is not generating any result even if I modify the query to a basic one like
Video.find({},function(err, data){
if(err){
response[currentValue] = "No data found";
}else{
response[currentValue] = data;
}
});
The response JSON is still returned blank.
PS: Problem has been simplified as is only an example of actual problem i am facing in the code.
update after answer found.
res.json(response)
was written outside the query that is why a blank json was getting returned. The above scenario can be solved using promise as follows:
var promise = Contact.find({ dept: { $in: arrayDept }}).exec();
promise.then(function(resultJson) { res.json(resultJson); });
This can be used to execute all the queries in the array and return the complete json.
Your res.json(response); in the non-working example is outside the callback of your MongoDB query, that is you are writing the response before the query's callback was actually executed and therefore your result is empty.

unable to add property to the json object

I am trying to add status to a response on successful update but I am not able to add the status property to json object of form. Here is my code
apiRouter.post('/forms/update', function(req, res){
if(req.body.id !== 'undefined' && req.body.id){
var condition = {'_id':req.body.id};
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form;
objForm.status = "saved successfully";
return res.send(objForm);
});
}else{
res.send("Requires form id");
}
});
and here is the response that I get, notice status is missing
{
"_id": "5580ab2045d6866f0e95da5f",
"test": "myname",
"data": "{\"name\":3321112,\"sdfsd\"344}",
"__v": 0,
"id": "5580ab2045d6866f0e95da5f"
}
I am not sure what I am missing.
Try to .toObject() the form:
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form.toObject();
objForm.status = "saved successfully";
return res.send(objForm);
});
Mongoose query result are not extensible (object are frozen or sealed), so you can't add more properties. To avoid that, you need to create a copy of the object and manipulate it:
var objectForm = Object.create(form);
objectForm.status = 'ok';
Update: My answer is old and worked fine, but i will put the same using ES6 syntax
const objectForm = Object.create({}, form, { status: 'ok' });
Another way using spread operator:
const objectForm = { ...form, status: 'ok' }
Try changing res.send(objForm) to res.send(JSON.stringify(objForm)). My suspicion is that the the Mongoose model has a custom toJson function so that when you are returning it, it is transforming the response in some way.
Hopefully the above helps.
Create empty object and add all properties to it:
const data = {};
data._id = yourObject._id; // etc
data.status = "whatever";
return res.send(data);
Just create a container.
array = {};
Model.findOneAndUpdate(condition, function(err, docs){
array = docs;
array[0].someField ="Other";
});

Resources