How to properly display a list of item in express - javascript

Good day everyone,
This app works fine when running in my laptop, but when launched from my tablet, the following code leads to a connection error. I am assuming its because the following requires too much processing power for the tablet to handle. Hence, I am looking for ways to optimize the following code.
Essentially I am trying to display a list of item for the user to select. The list in this case is the longestdimensions such as 11, 18, 24.
I am using express app, and for the view I am using handlebars (hbs).
Below is the route code
connection.connect();
// Run the query to retrieve the box name where the dimension of the box is equivilant to the computer name of the tablet
// The computer name of the tablet should reflect the location of the box which is indentified by
let sql = `SELECT box_id, longestDimension
FROM box
WHERE longestDimension != ''
AND LOWER(box_id) = LOWER(?)`;
connection.query(sql, computerName, function(err, rows, fields) {
if (!err) {
// Check to see if the user entered code is found in the database
// Create a variable to track if the item was found
for(var i=0; i< rows.length; i++) {
var data = {
rows: rows,
userHashtag: userEnteredHashtag
}
res.render('delivery/chooseBox', data);
}
// If the query fails to execute
} else {
console.log('Error while performing Query.');
res.render('error/errorConnection', {});
}
});
connection.end();
Below is the view code
{{#each rows}}
<form method="post" action="/delivery/chooseBoxSelected">
<input type="hidden" name="boxSelectedValue" value="{{this.box_id}}">
<input type="hidden" name="boxSelectedDimension" value="{{this.longestDimension}}">
<input type="hidden" name="userHashtag" value="{{userHashtag}}">
<button class="btn-dimension" type="submit">
<i class="fa fa-cube" aria-hidden="true"></i>
Longest dimension {{this.longestDimension}}"
</button>
</form>
{{/each}

For starters, you should only call res.render() once rather than separately for each row.
connection.query(sql, computerName, function (err, rows, fields) {
if (!err) {
// Check to see if the user entered code is found in the database
var data = {
rows: rows,
userHashtag: userEnteredHashtag
}
res.render('delivery/chooseBox', data);
// If the query fails to execute
} else {
console.log('Error while performing Query.');
res.render('error/errorConnection', {});
}
});
The {{#each rows}} in your template will repeat for every row. There is no need to call res.render() for each row. In fact, doing so is just a waste and may even cause a server error.

Related

How do I get the next row of database results from a button click using Node.js?

I'm using Node.js to serve my webpage and get the database results from mysql. Right now I have it successfully connected and showing the first row of data.(I'm using these as button values in the html code below) However, I want the user to be able to click a button and get the next row of data.
var express = require("express");
var app = express();
var mysql = require('mysql');
var con = mysql.createConnection({
// working connection
});
con.connect(function(err) {
if (err) throw err;
con.query("SELECT * FROM my_table limit 10", function (err, result, fields) {
if (err) throw err;
else{
sandbox.big1Value = result[0].big1;
sandbox.big2Value = result[0].big2;
}
});
});
app.get('/mypage', function (req, res) {
res.render('mypage', { dbinput: sandbox });
});
<button type="button" id="big1" onclick="myFunction(this.value)" value=<%= dbinput.big1Value %> ><%= dbinput.big1Value %> </button>
<button type="button" id="big2" onclick="myFunction(this.value)" value=<%= dbinput.big2Value %>><%= dbinput.big2Value %> </button>
<!-- I want this button to get the next set of values -->
<button type="button" id="next" onclick="doSomething()" value="Next">Next One</button>
You can do this using limit and offset in mysql From documentation
Syntax: LIMIT {[offset,] row_count | row_count OFFSET offset}
Here in your case you can use:
SELECT * FROM my_table LIMIT 10, 10; # Retrieve rows 11-20

Get and display data from API using JQuery

So guys, I need to get this API data: https://api.github.com and put it on a HTML file.
So here is the JavaScript code I'm writing:
async function getData()
{
//await the response of the fetch call
let response = await fetch('https://api.github.com');
//proceed once the first promise is resolved.
let data = await response.json()
//proceed only when the second promise is resolved
let newData = data.results;
return newData;
}
//call getData function
getData()
.then(function(result){
$.each(result, (index, user) => {
$('#character').append(`
<div class='card'>
<img
src=${user.image}
alt=''
class='round-img'
/>
<h4 class='card-name'>${user.name}</h4>
<input id="collapsible" class="toggle" type="checkbox">
<label for="collapsible" class="lbl-toggle">
<i class='fas fa-chevron-down'> </i>
</label>
</input>
</div>
`)
});
})
So I'm getting the users info, but I need to display it on a list with limited quantity of users per page. How can I do that?
If you can use an external library, then Data Table is one good library that will give you search, filter, sort, and pagination capability. you just neet to create the table and pass the id of that table to the Data Table library. It will take care of your requirements. You can set max items per page.
https://datatables.net/examples/basic_init/zero_configuration.html
instead of .each(), loop 'result' with a for loop.
var i;
for(i=0;i<(offset+(page*8)+1);i++){
//use result[i] in your html
}
offset if how many records you are in (so 0 for first page)
page is page num (so 1 for first page)
Looking at the documentation you provided.
The API will automatically paginate the responses. You will receive up to 20 documents per page.
The info field on the response will give you some useful information about the dataset, including how many pages there are, the next page & the count of documents.
info: {
count: 493,
pages: 25,
next: "https://rickandmortyapi.com/api/character/?page=2",
prev: ""
}

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.

removing tablet from and array mean stack

Hi I was wondering if someone could help me remove tablets from a container(array) I added them to in the MEAN stack.
My backend Container Controller
exports.updateContainer = function (req, res) {
console.log("container update backend controller called", req.body);
Container.findOneAndUpdate({ _id: req.params.id }, { $set: req.body }, { 'new': true })
.then( function (container) {
console.log("container",container);
if (container != null) {
return res.json(container);
}
})
.catch( function (err) {
return res.json(err);
});
My frond end add to container controller works, I'm having trouble with deleting them,looking at examples online i see the splice method being called a lot but I cant get it to work, say my logic is probably wrong anyway, I'm really struggling with the MEAN stack and JavaScript in general
$scope.addTabletToContainer = function(tablet) {
var container = $scope.currentContainer;
console.log("**** container,", container);
console.log("**** tablet to add", tablet);
container.tablets.push(tablet);
console.log("new tablets", container.tablets);
containerService.updateContainer(container)
.success(function(data) {
console.log("data, ", data);
})
.error(function (err) {
$location.path("./landingpage");
});
};
// not working yet
$scope.removeTabletFromContainer = function(tablet){
var container = $scope.currentContainer;
var index = container.tablets.indexOf(tablet);
container.splice(index,1);
containerService.updateContainer(container)
.success(function(data) {
console.log("data, ", data);
})
.error(function (err) {
$location.path("./landingpage");
});
};
Container end points in my routes file
var ContainerApi = require('./api/tablet/controller/container');
api.get('/getContainers', ContainerApi.getContainers);
api.get('/getContainer/:id', ContainerApi.getContainer);
api.delete('/deleteContainer/:id', ContainerApi.deleteContainer);
api.post('/createContainer', ContainerApi.createContainer);
api.put('/updateContainer/:id', ContainerApi.updateContainer);
A snippet of my html
<ul class="tablets" >
<li id="tabsincontainer" ng-repeat="tablet in currentContainer.tablets | filter:{name: query } | orderBy:orderProp" class="thumbnail">
<b> Name:</b> {{tablet.name}} ,
<b> Dose:</b> {{tablet.dose}} ,
<b> Amount To Take:</b> {{tablet.amountToTake}} ,
<b> Total Amount:</b> {{tablet.totalAmount}}
<button class="btn btn-xs pull-right btn-danger" ng-click="removeTabletFromContainer(tablet)">Remove</button>
</div>
I get this error at the moment
angular.js:9937 TypeError: container.splice is not a function
at a.$$childScopeClass.$$childScopeClass.$scope.removeTabletFromContainer (viewContainersController.js:90)
I think you need to splice from container.tablets like
container.tablets.splice(index,1);
The error message tells you that splice is either not a member of container, or, if it is, it is not a function. Since splice is a function of arrays, your problem essentially is that container is not an array. Take a look at the definition of container:
var container = $scope.currentContainer;
container is not an array, since in that case you could not get the error message you have shown us, therefore you cannot call its splice method. Since you intend to remove from the tablets, let's take a look at what tablets are in your case. Since tablets is a member of container and has an indexOf and a push function, it is safe to assume that tablets is the array you are looking for, so you will need to call container.tablets.splice instead of container.splice.

Weird issue where after inserting a doc, it exists for an instant, and then deletes itself?

They way I'm testing this is a simple for loop in the template to run through the elements available to the client and display them in a list.
I insert the elements through a text input identified by #query.
When I enter an element, it displays for a brief instant, and a console log that prints out Links.find().fetch() shows that the element exists, and then shortly afterwards, the element is seemingly automagically removed making any successive calls to Links.find().fetch() yield an empty list. Is this a bug within Meteor? Or is it expected behaviour and bad implementation?
UPDATE
Another weird development, I added setTimeout(function(){Links.find().fetch()},3000); to the server side to try and track what was going on. With this line, the inserts work correctly for a while, and then crashes with these errors: http://i.imgur.com/CUYDO67.png
. What is going on?
Below is my template file myapp.html
<head>
<title>myapp</title>
</head>
<body>
{{> search_bar}}
<br>
{{> list_of_links}}
</body>
<template name="search_bar">
<h1>Playlist</h1>
<input id="query" type="text" placeholder="Enter Query Here"/>
</template>
<template name="list_of_links">
<ul id="item-list">
{{#each my_playlist}}
{{> link_item}}
{{/each}}
</ul>
</template>
<template name="link_item">
<li class="link">
<div class="link-title">{{youtube_link}} {{sess}}</div>
</li>
</template>
And here follows myapp.js
//Setting up a collection of urls
Links = new Meteor.Collection("links");
if (Meteor.isClient) {
//"Subscribing" to server's published data
Deps.autorun( function(){
Meteor.subscribe( "links", Meteor.default_connection._lastSessionId);
});
//Nuke database helper function -- debugging
Template.list_of_links.clean = function(collection) {
if(collection) {
// clean items
_.each(collection.find().fetch(), function(item){
collection.remove({_id: item._id});
});
}
}
//Songs from session
Template.list_of_links.my_playlist = function () {
return Links.find();
};
Template.search_bar.events({
//http://stackoverflow.com/a/13945912/765409
'keypress #query' : function (evt,template) {
// template data, if any, is available in 'this'
if (evt.which === 13){
var url = template.find('#query').value;
//Find a nicer way of clearing shit.
$("#query").val('');
Links.insert({sess:Meteor.default_connection._lastSessionId,youtube_link:url});
var cursor = Links.find();
cursor.rewind();
console.log(cursor.fetch());
//Add to database.
}
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Meteor.publish("links", function( sess ) {
return Links.find({sess: sess}); //each client will only have links with that _lastSessionId
});
//Making sure permissions are correct
Links.allow({
insert: function (userId, doc) {
return true;
}
});
});
}
That kind of behavior is expected when user doesn't have enough privileges to create a document. The insert function creates a local copy of the doc instantly (thanks to latency compensation), and then sync it with the result of server operation. If that operation fails, the temporary document is purged from client's Minimongo.
Have you created proper rules with Collection.allow? That's the first place to look for the cause.

Categories