removing tablet from and array mean stack - javascript

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.

Related

SOLVED: JQuery reading EJS variable as a String and not the value of the EJS request

Alright, so I'm having a bit of a problem. I have an app that displays some facts via a search input. If that fact isn't existing, I want to display an error.
I'm producing that error via Node on the backend, and via EJS sends the error message to the HTML (.ejs) and javascript files.
Long story short, the error message displays correctly, but the error popup also displays when refreshing the page, even though there isn't any errors to display.
Error.js
var clientError = "<%=clientError%>"; //<--- (1) see comment below
$("#error").hide();
if(clientError !== "") { //<--- (2) see comment below
$("#error").fadeIn().show();
setTimeout(function(){
$("#error").fadeOut().hide();
}, 4000);
}
(1) This is being interpreted as the word "clientError" and characters "<%=%>" of "<%=clientError%>", and NOT the value of the .ejs query, for example, "An error occurred". This leads to problem no. 2, see below.
(2) Because "<%=clientError%>" isn't being read as an empty string, even if there aren't any errormessages, it runs the code either way and displays the error-popup. So when I refresh the website I get the popup, because the string isn't empty (even though it doesn't display any message, because there aren't any errors).
I have also tried some other variants of the error.js code, for example:
if(clientError.length >= 17) ...executes error popup // no luck with this either, see comment 1 and 2 above.
//I have also tried not defining clientError in App.js:
var clientError;
//And then inside error.js:
if(clientError !== undefined) ...executes error popup //no luck with this, since error.js reads it as a defined string.
App.js
var clientError = ""; //<-- (3)
...
...
app.get("/:option/:input", function(req, res) {
var opt = req.params.option;
var inp = req.params.input;
Article.find({
option: lodash.capitalize(opt),
input: lodash.capitalize(inp)
}, function(err, foundArticle) {
if (err) {
clientError = "Internal Server Error. Contact Administrator.";
setTimeout(function(){
clientError = "";
},4000);
console.log(err);
}
else if ((!foundArticle) || (foundArticle.length <= 0)) {
const notFound = new Notfound({
option: searchOptions,
input: searchInput
});
clientError = "Article not found. Try again."
setTimeout(function(){
clientError = "";
},4000);
res.redirect("/");
} else {
Article.findById(someIdOrSomething, function(err, someArticle){
res.render("page", {
...
clientError: clientError,
});
});
}
});
})
(3) An empty string. So the string in error.js should be read as an empty string, shouldn't it?
At last, we have the error.EJS
error.ejs
<div id="error" class="error-popup">
<h4>An error occurred.</h4>
<p id="errormessage"><%=clientError%></p>
</div>
One idea might be to have an input instead of the paragraph element above that's disabled as such...
<input id="errormessage" disabled type="text" value="<%=clientError%>">
... and then use Jquery to get the value of the input.
EDIT:
The idea above worked! Here is the new code:
error.js
$("#error").addClass("error-popup");
if($("#errormessage").val() !== "") {
$("#error").fadeIn().addClass("show-error-popup");
setTimeout(function(){
$("#error").fadeOut().removeClass("show-error-popup");
}, 4000);
}
error.ejs
<div id="error" class="error-popup">
<h4>An error occurred</h4>
<input id="errormessage" disabled type="text" value="<%=clientError%>">
</div>
Next step is just to style the input so it doesn't look like a regular input.

Why can't I pass functions from Express.js to EJS like I pass other variables?

I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.
Trying to paginate the posts I got stuck with passing newerPosts and olderPosts to the view. I am passing them just like the posts variable (and other variables):
exports.getPosts = async (req, res, next) => {
//pagination params
var perPage = 10;
var currPage = req.body.page ? parseInt(req.body.page) : 1;
const newerPosts = function() {
currPage = currPage + 1;
console.log(currPage);
}
const olderPosts = function() {
currPage = currPage - 1;
console.log(currPage);
}
const posts = await Post.find({}, (err, posts) => {
if (err) {
console.log('Error: ', err);
} else {
res.render('default/index', {
moment: moment,
layout: 'default/layout',
website_name: 'MEAN Blog',
page_heading: 'XPress News',
page_subheading: 'A MEAN Stack Blogging Application',
posts: posts,
newerPosts: newerPosts,
olderPosts: olderPosts
});
}
})
.sort({
created_at: -1
})
.skip((currPage - 1) * perPage)
.limit(perPage)
.populate('category');
};
In the view:
<div class="px-1">
<a class="btn btn-primary" href="#" onclick=<%="olderPosts()"%>>← Older Posts</a>
</div>
<div class="px-1">
<a class="btn btn-primary" href="#" onclick=<%="newerPosts()"%>>Newer Posts →</a>
</div>
Yet, I get the Uncaught ReferenceError: newerPosts is not defined error message from the browser.
What am I doing wrong? What is the closest working alternative?
You can do the following:
make a new ejs file called functions.ejs:
<script>
function test(){console.log(1)}
</script>
and then use <%- include('functions'); %> inside the template you are rendering. Use it at the start so that the function is defined when calling it.
In the corresponding route, use:
res.render('default/index', {
...,
name: 'test'
});
Inside your template can do the following:
<button onclick="<%=name%>()"> <%= btnText %> </button>
BUT, when I look at the function you want to pass, I see that you want to change the state of your application by using currPage = currPage + 1;. This wont work, since as soon as the page is rendered there is no reference to currPage in the html anymore, cause everything is static html by then.
If you want to change pages etc. you will need to implement some frontend logic.
Check this one:
const posts = await Post.find({}, (err, posts) => {
xxxx ... res.render()
})
I think here you need to make some changes in the logic of Promise usage ...
Think about - what happens if error appears ? where you handle it ?
and more interesting - after promise return result - where you exit from your function ? ...

How to properly display a list of item in express

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.

how to toggle particular div element in ng-repeat?

I have list of product , If I click on one product it needs to be added in database and again if I click on same product it should be removed from database, I am toggling wishlistFlag on ng-click. It should be fine on one div element, for second div element it is working reverse of first div element, I mean in case first product is added then if I click second product it has to be added but it is removing first product.
<div class="hmpal-prprt-post-wdgt hmpal-prprt-wishlist">
<a href="">
<span class="prprt-icon"><i class="fa fa-heart" aria-hidden="true"
ng-click="WishlistAdd($index,project.propertyId)"></i></span>
<span>Wishlist</span>
</a>
</div>
And inside Controller code is here,
a.WishlistAdd = function (index,propertyId) {
a.wishlistFlag = !a.wishlistFlag;
var data = {
wishlistFlag:a.wishlistFlag,
propertyId:propertyId
};
e.createWishLists(data).then(function (result) {
alert("sucesss");
alert(JSON.stringify(result));
}, function (error) {
alert("error");
alert(JSON.stringify(error));
});
}
How I can implement toggle wishlistFlag for different product .
Since you want at most one product selected in your wish list, instead of having a boolean flag, you can use the selected product id (called propertyId in your code if I'm right). The selectedProductId variable, defined in the controller, is either null if there is no product selected, or the product id if there is one.
The toggling is done by checking if the current selected id is equal to the clicked one.
I assume that
when wishListFlag === true in the sent data, you want to add the
product identified by propertyId, and otherwise, remove the
product from the database.
when you add a product, the server side
actually replace the selected product if there is an existing one.
// Is there a selected product ?
var selectedProductId = null;
a.WishlistAdd = function (index, propertyId) {
selectedProductId = (selectedProductId === propertyId ? null : propertyId);
var data = {
wishlistFlag: (selectedProductId !== null),
propertyId: propertyId
};
e.createWishLists(data).then(function (result) {
alert("sucesss");
alert(JSON.stringify(result));
}, function (error) {
alert("error");
alert(JSON.stringify(error));
});
}
You can make use of $index to have seperate flag to each of the wishList. Here, you can write your code as..
a.wishListFlag[index]
maintain a array of added propertyIds before save to the database check that id is in array if so remove from database and array else remove from database and array.
var wishList = [];
a.WishlistAdd = function (index,propertyId) {
var data = {
wishlistFlag: wishList.indexOf(propertyId) === -1,
propertyId:propertyId
};
e.createWishLists(data).then(function (result) {
//add or remove propertyid from whishList
if(data.wishlistFlag)
wishList.push(propertyId);
else
wishList.splice(wishList.indexOf(propertyId), 1);
alert("sucesss");
alert(JSON.stringify(result));
}, function (error) {
alert("error");
alert(JSON.stringify(error));
});
}

Having issues tying together basic javascript chat page

I have the skeleton of a chat page but am having issues tying it all together. What I'm trying to do is have messages sent to the server whenever the user clicks send, and also, for the messages shown to update every 3 seconds. Any insights, tips, or general comments would be much appreciated.
Issues right now:
When I fetch, I append the <ul class="messages"></ul> but don't want to reappend messages I've already fetched.
Make sure my chatSend is working correctly but if I run chatSend, then chatFetch, I don't retrieve the message I sent.
var input1 = document.getElementById('input1'), sendbutton = document.getElementById('sendbutton');
function IsEmpty(){
if (input1.value){
sendbutton.removeAttribute('disabled');
} else {
sendbutton.setAttribute('disabled', '');
}
}
input1.onkeyup = IsEmpty;
function chatFetch(){
$.ajax({
url: "https://api.parse.com/1/classes/chats",
dataType: "json",
method: "GET",
success: function(data){
$(".messages").clear();
for(var key in data) {
for(var i in data[key]){
console.log(data[key][i])
$(".messages").append("<li>"+data[key][i].text+"</li>");
}
}
}
})
}
function chatSend(){
$.ajax({
type: "POST",
url: "https://api.parse.com/1/classes/chats",
data: JSON.stringify({text: $('input1.draft').val()}),
success:function(message){
}
})
}
chatFetch();
$("#sendbutton").on('click',chatSend());
This seems like a pretty good project for Knockout.js, especially if you want to make sure you're not re-appending messages you've already sent. Since the library was meant in no small part for that sort of thing, I think it would make sense to leverage it to its full potential. So let's say that your API already takes care of limiting how many messages have come back, searching for the right messages, etc., and focus strictly on the UI. We can start with our Javascript view model of a chat message...
function IM(msg) {
var self = this;
self.username = ko.observable();
self.message = ko.observable();
self.timestamp = ko.observable();
}
This is taking a few liberties and assuming that you get back an IM object which has the name of the user sending the message, and the content, as well as a timestamp for the message. Probably not too far fetched to hope you have access to these data elements, right? Moving on to the large view model encapsulating your IMs...
function vm() {
var self = this;
self.messages = ko.observableArray([]);
self.message = ko.observable(new IM());
self.setup = function () {
self.chatFetch();
self.message().username([user current username] || '');
};
self.chatFetch = function () {
$.getJSON("https://api.parse.com/1/classes/chats", function(results){
for(var key in data) {
// parse your incoming data to get whatever elements you
// can matching the IM view model here then assign it as
// per these examples as closely as possible
var im = new IM();
im.username(data[key][i].username || '');
im.message(data[key][i].message || '');
im.timestamp(data[key][i].message || '');
// the ([JSON data] || '') defaults the property to an
// empty strings so it fails gracefully when no data is
// available to assign to it
self.messages.push(im);
}
});
};
}
All right, so we have out Javascript models which will update the screen via bindings (more on that in a bit) and we're getting and populating data. But how do we update and send IMs? Well, remember that self.message object? We get to use it now.
function vm() {
// ... our setup and initial get code
self.chatSend = function () {
var data = {
'user': self.message().username(),
'text': self.message().message(),
'time': new Date()
};
$.post("https://api.parse.com/1/classes/chats", data, function(result) {
// do whatever you want with the results, if anything
});
// now we update our current messages and load new ones
self.chatFetch();
};
}
All right, so how do we keep track of all of this? Through the magic of bindings. Well, it's not magic, it's pretty intense Javascript inside Knockout.js that listens for changes and the updates the elements accordingly, but you don't have to worry about that. You can just worry about your HTML which should look like this...
<div id="chat">
<ul data-bind="foreach: messages">
<li>
<span data-bind="text: username"></span> :
<span data-bind="text: message"></span> [
<span data-bind="text: timestamp"></span> ]
</li>
</ul>
</div>
<div id="chatInput">
<input data-bind="value: message" type="text" placeholder="message..." />
<button data-bind="click: $root.chatSend()">Send</button>
<div>
Now for the final step to populate your bindings and keep them updated, is to call your view model and its methods...
$(document).ready(function () {
var imVM = new vm();
// perform your initial search and setup
imVM.setup();
// apply the bindings and hook it all together
ko.applyBindings(imVM.messages, $('#chat')[0]);
ko.applyBindings(imVM.message, $('#chatInput')[0]);
// and now update the form every three seconds
setInterval(function() { imVM.chatFetch(); }, 3000);
});
So this should give you a pretty decent start on a chat system in an HTML page. I'll leave the validation, styling, and prettifying as an exercise to the programmer...

Categories