Edit on Express outputing JSON to database field - json

Trying to create my first simple CRUD in Express JS and I cant seem to find this annoying bug.
When I try to update a field, the JSON from that field, gets outputed to the view, instead of the new data.
Screenshot: http://i59.tinypic.com/wi5yj4.png
Controller gist: https://gist.github.com/tiansial/2ce28e3c9a25b251ff7c

The update method is used for finding and updating documents without returning the documents that are updated. Basically what you're doing is finding documents without updating them, since the first parameter of the update function is the search criteria. You need to use the save function to update an exiting document, after updating it's properties.
Your code below, modified (not tested):
//PUT to update a blob by ID
.put(function(req, res) {
//find the document by ID
mongoose.model('Email').findById(req.id, function (err, email) {
//add some logic to handle err
if (email) {
// Get our REST or form values. These rely on the "name" attributes
email.email = req.body.email;
email.password = req.body.password;
email.servico = req.body.servico;
//save the updated document
email.save(function (err) {
if (err) {
res.send("There was a problem updating the information to the database: " + err);
}
else {
//HTML responds by going back to the page or you can be fancy and create a new view that shows a success page.
res.format({
html: function(){
res.redirect("/emails");
},
//JSON responds showing the updated values
json: function(){
res.json(email);
}
});
}
});
}
});
})

Related

React - When user search an API in form, how to handle no matches in API?

I'm trying to build an weatherapplication for a schoolproject in React. I've gotten so far that when i search for London, it will show me the current temprature and other useful information i've chosen to display. I simply want a message to appear that the city could not be found, i guess this somehow should done in a variable and with help of If statements?
However im not sure how i handle if the user types something that that isnt there, for example "new jorc" instead of New York. My current code looks like this.
getWeather = async (e) => {
e.preventDefault();
const city= e.target.elements.city.value;
if(city){
const api_call = await fetch(
`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${API_KEY}`
);
const response = await api_call.json();
this.setState({
city: `${response.name}`,
celsius: this.evenDeagree(response.main.temp),
wind: this.evenDeagree(response.wind.speed),
feelsLike: this.evenDeagree(response.main.feels_like)
});
} else{
this.setState({error: true})
}
Any aid is appreciated.
A try/ catch block could work.
Wrap the call in the try block and put any error handling code you want in the catch block.
This way if the user types in "New Jork", the call will fail and enter the catch block. (You could also use .then/.catch instead as fetch returns a promise, up to you)
You should also check the response before setting it to the state, put something like this after response;
if (!response.ok) {
return "INPUT ERROR";
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch

How to use NODE to change some HTML before page load?

Hi I have been trying to change some of my HTML elements using NODE.
So I'm using Node modules request and cheerio to get specific html elements before the page loads.
What I want to achieve is to get some data from my database and modify the html before it loads. Here is what I have:
app.js:
router.get('/goToSettings', (req, res) => {
//get session id
const id = 1;
//prepare SQL query
const sqlQuery = "SELECT first_name, last_name, username, email FROM myTable WHERE id = ?";
//Get data from DB by using the id retrieved from SESSION
getConnection().query(sqlQuery, [id], async (err, results, fields) => {
//Catch error with MySQL connection
if(err){
console.log(" > The connection have failed :( >" + err);
res.sendStatus(500);
}
var url = "http://host.com/settings.html";
request(url, async function(error, response, html){
if(!error){
var $ = cheerio.load(html);
//DEFAULT VALUES FROM HTML:
//a.value = Johnny
//b.value = Blue
//c.value = j.b#email.com
//Set new values from my DB
var a = $('input.settings_1').val(results[0].first_name);
var b = $('input.settings_2').val(results[0].last_name);
var c = $('input.settings_3').val(results[0].email);
console.log(a.val()); //myNewFirstName
console.log(b.val()); //myNewLastName
console.log(c.val()); //myNewEmail
await res.redirect('/accountSettings.html'); //VALUES of a,b,c remain (Johnny, Blue, j.b#email.com)
}else{
console.log("WRONG URL");
}
});
});
});
On the console.log data seems to be changed, everything looks okay, but when the page is redirected everything is back to default. I tried loading the page before the changes with no luck. That's where I figure out I have to use await/async so that the changes are made and then the redirect is called, but again nothing changes. Does the redirection resets the html file when loading?
Someone else suggested to use AJAX to perform this task, but I have no previous experience with it. I guess if this is impossible I will try and use it. It has been also suggested to me, that this should not be the way (NODE SHOULD NOT HANDLE THIS), let me know if you agree.
SOLUTION:
//cmd
npm i ejs
//app.js
app.set('view engine', 'ejs');
//Save Data in list of objects
let objects = [
{ value: results[0].first_name },
{ value: results[0].last_name },
{ value: results[0].email }
];
//REDIRECT PAGE WITH SOME DATA
res.render('accountSettings', {object: objects})
//REMEMBER TO CHANGE HTML TO .ejs EXTENSION.
//MOVE settings.ejs TO DIR:
//yourAppDir > views > settings.js
//settings.ejs
<input value="<%= object[0].value %>">
<input value="<%= object[1].value %>">
<input value="<%= object[2].value %>">
The core reason your approach isn't working is that you are creating a DOM from the contents of settings.html, manipulating it, but never saving it back to the file.
However, the approach itself is horribly flawed. It is inefficient and subject to race conditions. You need to change your approach.
Get rid of cheerio
Convert the HTML document to a template. There are plenty to choose from.
Replace the redirect with a call to render that passes your data into the template:
Such:
res.render('accountSettings.ejs', { settings: results[0] });

Redirect issues when using MySQL as session storage

I have set up a Node.js app where I use sessions and store them in MySQL. When using MemoryStorage, redirections work fine, but when using MySQL, req.session doesn't update until you reload or you move to a different page, and I'm forced to replace every single res.redirect('/...') by res.render() of that same page to display anything in req.session immediately.
I've tried using both return res.redirect() and not, as well as using setTimeout, neither work. I can't figure it out and I need sessions to be stored in DB
router.get('/student-sign-up', function (req, res, next) {
res.render('student/signUp', {
title: 'Sign up',
errors: req.session.errors
});
req.session.errors = null; //to flush them on reload
}).post('/student-sign-up', function (req, res, next) {
//Some form checks
let errors = req.validationErrors();
if (errors) {
req.session.errors = errors;
req.session.signUpSuccess = false;
return res.redirect('/student-sign-up');
}
//...
}
The above should redirect to the same page, and display the error (I use Handlebars as my view engine) if there were one, but it simply redirects, and if you refresh manually or submit a faulty from again, then it displays it. Same thing for logins (both success not going into the platform's home, and failure not showing errors either). It's like everything's lagging behind by 1 step...
OK, I found the solution. According to the express-session docs, all I had to do was force a save and then redirect, as so:
req.session.save((err) => {
if (err) {
req.locals.error = err;
return res.redirect('/');
}
return res.redirect('/next-section');
});
I'll leave this here for anyone that might have the same issue!

Slow Loading of Large MongoDB Database in Node JS

I have created a webpage with Node JS, Express JS, Mongoose and D3 JS.
In the webpage, it contains 3 pull down menus: Department, Employee, Week.
The usage of the webpage is as follows:
When 'Department' is selected, 'Employee' menu will be filtered to show only those from the selected 'Department'. The same goes to 'Week' after 'Employee' is selected.
After the 3 menus are selected and 'PLOT' button is clicked, a line chart (using d3.js) will be plotted to show the employee working hours for the month.
MongoDB Json
{ dep: '1',
emp: 'Mr A',
week: 1,
hrs: [{
{1,8},
{2,10},
...
}]
}
Here are the snippets of my codes:
routes.js
// Connect the required database and collection
var dataAll = require('./models/dataModel');
module.exports = function(app) {
app.get('/api/data', function(req, res) {
dataAll.find({}, {}, function(err, dataRes) {
res.json(dataRes);
});
}
app.get('*', function(req,res) {
res.sendfile('./index.html');
}
}
index.html
... // More codes
<div id="menuSelect1"></div>
<div id="menuSelect2"></div>
<div id="menuSelect3"></div>
...
<script src="./display.js" type='text/javascript'></script>
... // More codes
display.js
//Menu (Department,Employee,Week) Information is gathered here
queue()
.defer(d3.json, "/api/data")
.await(createPlot);
function createPlot(error, plotData) {
var myData = plotData;
var depData = d3.nest()
.key(function(d) {return d.dep;})
.rollup(function(v) {return v.length;})
.entries(myData);
selectField1 = d3.select('#menuSelect1')
.append("select")
.on("change", menu1change)
.selectAll(depData)
.enter()
.append("option")
.attr("value", function(d) {return d.key;})
.text(function(d) {return d.key;});
function menu1Change() {
//Filter Next Menu with the option chosen in this menu
... // More codes
var selectedVal = this.options[this.selectedIndex].value;
var empData = dataSet.filter(function(d) { return d.emp = selectString; });
... // More codes
}
... // More codes
}
Problem:
Functionally, it is working as expected. Problem is when the database is getting larger and larger, the loading of the page becomes very very slow (mins to load). I believe it should be due to the routing where all data is retrieved (.find({},{})) but I thought I need it because I am using it in 'display.js' to filter my menu options.
Is there a better way to do this to resolve the performance issue?
It is rarely necessary to send all the data to the client. In fact, I haven't seen an API with a single endpoint that returns the entire database to everyone.
It's hard to give you any specific solution not knowing how your data looks like, how large it is, how fast it grows etc. The performance issues may be related to querying the database, to large data transfer, or large JSON to parse by the browser.
In any case, you shouldn't send all your database to the client with no limits. Usually it is implemented with a number of records to skip and a maximum number of records to return.
Some frameworks like LoopBack does it for you, see:
https://docs.strongloop.com/display/public/LB/Skip+filter
https://docs.strongloop.com/display/public/LB/Limit+filter
If you're using Express then you'll have to implement the limits yourself.
To test the bottleneck, you can run the Mongo shell and try to run the .find({},{}) query from there to see how long it takes. You can see the transfer size and time in the browser's developer tools. This may find you narrow down the place that needs most attention, but returning the entire database no matter how large it is, is already a good place to start.

How to create an angular form that uses session storage that can be called throughout the html pages

I want to create a form on an index page that can store data via session storage. I also want to make sure that whatever data(let's say name) ... is remembered and used throughout the site with angular. I have researched pieces of this process but I do not understand how to write it or really even what it's called.
Any help in the right direction would be useful as I am in the infant stages of all of this angular business. Let me know.
The service you want is angular-local-storage.
Just configure it in your app.js file:
localStorageServiceProvider
.setStorageType('sessionStorage');
And then use it in the controller that contains whatever data you want to remember. Here is an example of a controller that loads the session storage data on initialization, and saves it when a user fires $scope.doSearch through the UI. This should give you a good place to start.
(function () {
angular.module("pstat")
.controller("homeCtrl", homeCtrl);
homeCtrl.$inject = ['$log', 'dataService', 'localStorageService', '$http'];
function homeCtrl ($log, dataService, localStorageService, $http) { {
if (localStorageService.get("query")) { //Returns null for missing 'query' cookie
//Or store the results directly if they aren't too large
//Do something with your saved query on page load, probably get data
//Example:
dataService.getData(query)
.success( function (data) {})
.error( function (err) {})
}
$scope.doSearch = function (query) {
vm.token = localStorageService.set("query", query);
//Then actually do your search
}
})
}()