M.E.A.N put not updating - javascript

I have an array of comments on a picture called 'comments'
I also have a property called "newcmt" that i'd like to push into comments
I have a function addComment that tries and fails to make these changes.
I have tried a few different ways, but the put doesn't seem to update the api despite returning the proper changes in the response.
Am I just updating the reference?
Is asynchronous nature causing errors?
Is my mongo scheme wrong?
Would it be easier to make comments its own endpoint?
JS
var refresh = function() {
$http.get('/api/things').success(function(awesomeThings) {
$scope.awesomeThings = awesomeThings;
});
};
$scope.addThing = function() {
if($scope.newThing === '') {
return;
}
$http.post('/api/things/', { name: $scope.newThing });
refresh();
};
$scope.addComment = function(thing) {
if(thing.newcmt == '') {
return;
}
$scope.newcmt = thing.comment;
$scope.newcmt.push(thing.newcmt);
console.log($scope.newcmt)
$http.put('/api/things/' + thing._id, {comment : $scope.newcmt }, {safe: true, upsert: true, new : true}).success(function(response) {
console.log(response);
refresh();
})
};
$scope.deleteThing = function(thing) {
$http.delete('/api/things/' + thing._id);
refresh();
};
HTML
<div class="container">
<img class="img-responsive" src="{{thing.url}}" alt="">
</div>
<div class="col-md-12" ng-repeat="comment in thing.comment">
<p>{{comment}}</p>
</div>
<div class="row">
<div class="col-xs-12">
<i class="fa fa-comment-o fa-3x"></i>
<input type="text" placeholder='...' ng-model="thing.newcmt" >
<button class="btn btn-default" ng-click="addComment(thing)">+</button>
<i class="fa fa-arrow-down fa-2x pull-right"></i>
<i class="fa fa-arrow-up fa-2x pull-right"></i>
</div>
</div>
MODEL
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ThingSchema = new Schema({
name: String,
url: String,
credit: String,
active: Boolean,
comment: Array,
newcmt: String
});
module.exports = mongoose.model('Thing', ThingSchema);
PUT
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.status(404).send('Not Found'); }
var updated = _.merge(thing, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.status(200).json(thing);
});
});
};
full github:
https://github.com/jneljneljnel/meangen

Related

My Vue Js app refreshes on each single doc change

I'm using Vue Js and Firebase to build a voting web app, the app contains units, and each unit has an "upvote" and "downvote" buttons.
my problem is that whenever a button is clicked the whole list of units refreshes, instead of changing the votes count only as it should. am I missing something with my Vue js code?
Html code:
<div id="unitWrapper">
<div v-for="request in requests" id="unit" >
<iframe :src="request.text" id="spotifyEmbed" width="80" height="80" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
<h2 id="title">{{ request.songN }}</h2>
<div id="votes">
<span id="up" #click="upV(request.id)">
<i class="fas fa-arrow-up fa-2x" v-if="request.isUpvoted" style="color: rgb(1, 187, 1);"></i>
<i class="fas fa-arrow-up fa-2x" v-else style="color: #888;"></i>
<p class="votesNumber" style="color: rgb(1, 187, 1);">{{ request.upVotes }}</p>
</span>
<span id="down" #click="downV(request.id)">
<i class="fas fa-arrow-down fa-2x" v-if="request.isDownvoted" style="color: red;"></i>
<i class="fas fa-arrow-down fa-2x" v-else style="color: #888;"></i>
<p class="votesNumber" style="color: red;">{{ request.downVotes }}</p>
</span>
</div>
</div>
</div>
Js/Vue code:
var app = new Vue({
el: "#unitWrapper",
data: {
requests: [],
},
methods: {
upV(id) {
const upvote = firebase.functions().httpsCallable("upvote");
upvote({ id: id }).catch((err) => {
showNotification();
});
},
downV(id) {
const downvote = firebase.functions().httpsCallable("downvote");
downvote({ id: id }).catch((err) => {
showNotification();
});
},
},
created() {
const ref = firebase
.firestore()
.collection("requests")
.orderBy("upVotes", "desc");
ref.onSnapshot((snapshot) => {
let requests = [];
snapshot.forEach((doc) => {
var obj = {};
obj = {
...doc.data(),
id: doc.id,
};
var user = firebase.auth().currentUser;
if (user != null) {
firebase
.firestore()
.collection("users")
.doc(user.uid)
.get()
.then((val) => {
if (val.exists) {
var res = val.data().upVotedOn.includes(doc.id)
var res2 = val.data().downVotedOn.includes(doc.id)
console.log(`${res2} - downvoted`);
obj.isUpvoted = res;
obj.isDownvoted = res2;
} else {
obj.isUpvoted = false;
obj.isDownvoted = false;
}
requests.push(obj);
})
.catch((err) => console.log(err));
} else {
obj.isUpvoted = false;
obj.isDownvoted = false;
requests.push(obj);
}
});
// console.log(requests);
this.requests = requests;
});
},
});
any help is appreciated

How to use spinner to show axios status on a specific button

i am using a v-for to display list of product from an api request, the product card contains three buttons, one of the Adds item to cart,with a shopping-cart icon.
i want it so that when a user clicks the add to cart button, the shopping-cart icon changes to a spinner icon
I try declaring a "loading" in the data object, default set to false, so in my add to cart function, before the function is called, loading is set to true,
And in my template i use a v-show="loading" which set the visibility of the fa-spin to true if loading is true
//template
<template>
<div class="row">
<div v-for="product in products" v-bind:key="product_slug"
class="col-md-auto mx-auto card text-center card-product">
<div class="card-product__img">
<img class="card-img" src="img/product/product1.png" alt="">
<ul class="card-product__imgOverlay">
<li>
<button><i class="ti-search"></i></button>
</li>
<li>
<button #click="addToCart(product.id, product.slug, product.price)"><i
class="ti-shopping-cart"></i> <i v-show="loading" class="fa fa-spinner fa-spin"></i>
</button>
</li>
<li>
<button><i class="ti-heart"></i></button>
</li>
</ul>
</div>
<div class="card-body">
<p>Accessories</p>
<h4 class="card-product__title">{{ product.slug }}</h4>
<p class="card-product__price">₦ {{ product.price}}</p>
</div>
</div>
//script
<script>
export default {
data() {
return {
loading: false,
products: [],
product: {
"id": '',
"slug": '',
"product_image_1": '',
"product_image_2": '',
"product_image_3": '',
"product_image_4": '',
"price": '',
"qty": '',
"stock_status": '',
"sku": '',
"short_description": '',
"description": '',
},
product_slug: '',
pagination: {},
}
},
created() {
this.fetchProduct();
},
methods: {
fetchProduct(page_url) {
//assign variable to this
let vm = this;
// check if page url exist, = page url else = /api/shop
page_url = page_url || '/api/shop';
fetch(page_url)
.then(res => res.json())
.then(res => {
this.products = res.data;
vm.makePagination(res.links, res.meta);
})
.catch(err => console.log(err));
},
makePagination(links, meta) {
//Make an object made up of meta, page details from the api response
let pagination = {
current_page: meta.current_page,
last_page: meta.last_page,
next_page_url: links.next,
prev_page_url: links.prev,
};
// Set the object to the pagination value
this.pagination = pagination;
},
addToCart(id, slug, price) {
this.loading = true;
axios.post('/api/cart', {
id: id,
name: slug,
price: price,
})
.then(function (response) {
this.loading = false;
console.log(response.data);
})
.catch(function (err) {
this.loading = false;
this.addToCart = err;
});
}
}
}
</script>
The problems are
1) Once the add to cart button is clicked, the spinner shows in all of the product's card.
2) fa-cart icon is not hiding, shows side-by-side with the shopping-cart icon
3) fa-spin continues, even after success of api request
You need to maintain a dictionary of the loading state. In addToCart function, you need to set true for particular product id. Try this code.
addToCart(id, slug, price) {
this.loading[id] = true;
axios.post('/api/cart', {
id: id,
name: slug,
price: price,
})
.then(function (response) {
this.loading[id] = false;
console.log(response.data);
})
.catch(function (err) {
this.loading[id] = false;
this.addToCart = err;
});
}
In Fetch product function made some changes.
fetchProduct(page_url) {
//assign variable to this
let vm = this;
// check if page url exist, = page url else = /api/shop
page_url = page_url || '/api/shop';
fetch(page_url)
.then(res => res.json())
.then(res => {
this.products = res.data;
this.products.filter(function (item) {
vm.loading[item.id]=false;
return item;
})
vm.makePagination(res.links, res.meta);
})
.catch(err => console.log(err));
},
html changes.
<button #click="addToCart(product.id, product.slug, product.price)"><i
class="ti-shopping-cart"></i> <i v-show="loading[product.id]" class="fa fa-spinner fa-spin"></i>
</button>

Delete item from nested array in Schema using moongoose

I need help with removing this item from nested array. I tried used $http.delete but this method deleted whole ObjectID from database, and second problem is that I can't connect the click on the "removing" button with backend code.
My code:
var product = new Schema({
title: String,
price: String,
description: [ObjectID]
});
Index.html
<form name="editProduct.descriptionForm" ng-submit="editProduct.updateDescription(newDescription, editProduct.descriptionForm.description.$valid)" novalidate>
<div ng-class="{ 'has-success':(editProduct.descriptionForm.description.$valid && !editProduct.descriptionForm.description.$pristine), 'has-error':(!editProduct.descriptionForm.description.$valid && !editProduct.descriptionForm.description.$pristine) || (!descriptionForm.description.$valid && descriptionForm.$submitted) }">
<div class="entry input-group" ng-repeat="item in newDescription track by $index">
<strong><input ng-disabled="editProduct.disabled" class="form-control" type="text" name="description" ng-model="newDescription[$index]" required></strong>
<span class="input-group-btn">
<a ng-click="editProduct.deleteDescription(item);" class="btn btn-remove btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
</span>
</div>
</div>
<br>
<button ng-disabled="editProduct.disabled" class="btn btn-primary" type="submit">Save</button>
</form>
routes.js
router.put('/editProduct', function(req, res){
var editProduct = req.body._id;
var options = { multi: true };
Product.findOneAndUpdate({ _id: editProduct }, { $pull: { description: req.params.description }}, options, function(err, product){
if(err) throw err;
if(!product){
res.json({ success: false, message: 'Product no found' });
} else {
product.update(function(err){
if(err){
console.log(err);
} else {
res.json({ success: true, message: 'Description removed!'})
}
});
};
});
});
I also tried the following approach:
Product.findOne({'_id' : product.id}, function(err, me){
for(var i=0; i<=me.description.length; i++){
if (String(me.description[i])==String(uid)){
me.description.remove(uid);
me.save();
}
}
});
I think, the biggest problem is that I don't how to connect this function to the button.
Please try console.log(req.params.description) Before the Mongoose update query and check if the output is indeed a valid ObjectId.
If the console output is not showing the valid uid, then the problem is in the angular code. Most probably in editProduct.deleteDescription(item) function. Check if you are making Http Request by passing the correct Description Id as the parameter. Thats probably something like item.descriptionId or item.id. Debug thoroughly.

How do I render a handlebars layout in node.js from a button?

This is a really weird issue I am having.
I have a login form, this login form verifies your data and renders the Profile layout if the login is successful OR renders the register page if the login is not.
exports.logIn = function (req, res, data) {
var username = req.body.username.toString();
var password = req.body.password.toString();
connection.connection();
global.connection.query('SELECT * FROM Utilizador WHERE Nome_Utilizador = ? LIMIT 1', [username], function (err, result) {
if (result.length > 0) {
if (result) {
var object = JSON.parse(JSON.stringify(result));
var userObject = object[0];
var userQ = object[0].Nome_Utilizador;
global.connection.query('SELECT Password_Utilizador from Utilizador where Nome_Utilizador = ?', [username], function (err, result) {
console.log(result);
if (result.length > 0) {
if (result) {
var object2 = JSON.parse(JSON.stringify(result));
var passQ = object[0].Password_Utilizador;
if (password == passQ) {
console.log("Login efectuado com sucesso");
console.log(userObject);
res.render('home', { title: 'perfil', layout: 'perfil', data: userObject });
} else {
console.log("1");
}
}
} else if (err) {
console.log("asdsadas");
} else {
console.log("2");
res.render('home', { title: 'perfil', layout: 'registo' });
}
});
}
} else if (err) {
console.log(err);
} else {
console.log("Utilizador nao encontrado");
res.render('home', { title: 'perfil', layout: 'registo' });
}
});
};
This works.
And the only reason why it does work is because it comes from a FORM with a METHOD and an ACTION
<form id="login-nav" action="/login" method='POST' role="form" accept-charset="UTF-8" class="form">
<div class="form-group">
<label for="username" class="sr-only">Utilizador</label>
<input id="username" type="username" placeholder="Nome de utilizador" required="" class="form-control" name="username">
</div>
<div class="form-group">
<label for="exampleInputPassword2" class="sr-only">Palavra-Passe</label>
<input id="password" type="password" placeholder="Meta a palavra-passe" required="" class="form-control" name="password">
</div>
<div class="checkbox">
<label></label>
<input type="checkbox">Gravar Dados
</div>
<div class="form-group">
<button id="botaoLogin" class="btn btn-danger btn-block">Fazer Login</button>
</div>
</form>
However, I tried to do the same thing with jQuery, as I need to render a Handlebars layout for some products on button click,
$("#pacotes").on('click', ".produto", function () {
var prod = this.id;
console.log(prod);
$.get("http://localhost:3000/pacote?idPacote=" + prod);
});
And despite the query working and giving me the data I requested
exports.Pacote = function (req, res) {
var pacote = req.query.idPacote;
connection.connection();
global.connection.query('SELECT * FROM Pacotes WHERE idPacotes = ? ', [pacote], function (err, result) {
if (result.length > 0) {
if (result) {
var object = JSON.parse(JSON.stringify(result));
var packObject = object[0];
console.log(result);
res.render('home', { title: 'pacote', layout: 'pacote', data: packObject });
} else if (err) {
console.log(err);
}
};
});
}
It simply doesn't render the layout and I have no idea why.
What is the difference between doing a POST request like this or doing it by a form?
I don't understand why this only seems to work with forms.
I could solve it that way, but I don't think using empty forms for all my buttons would be a viable solution.
You are only making a request, you are not processing the return value:
$.get("http://localhost:3000/pacote?idPacote=" + prod);
Try changing to something like:
$.ajax({
method: 'GET',
url: "http://localhost:3000/pacote?idPacote=" + prod,
success: function(...) {...}
});

Node/AngularJS - TypeError: Cannot read property '_id' of undefined.

I am new to the MEAN and i am trying to make a simple CRUD application. I am getting an error of undefined on my _id and i do not understand why. This variable works withevery other function I call it in. Hopefully someone can help. I am getting the error on line 117 in my controller.js file
Here is the controller.js code for my application
todoApp.controller('TodoCtrl', function($rootScope, $scope, todosFactory) {
$scope.todos = [];
$scope.isEditable = [];
// get all Todos on Load
todosFactory.getTodos().then(function(data) {
$scope.todos = data.data;
});
// Save a Todo to the server
$scope.save = function($event) {
if ($event.which == 13 && $scope.todoInput) {
todosFactory.saveTodo({
"todo": $scope.todoInput,
"isCompleted": false
}).then(function(data) {
$scope.todos.push(data.data);
});
$scope.todoInput = '';
}
};
//update the status of the Todo
$scope.updateStatus = function($event, _id, i) {
var cbk = $event.target.checked;
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _id,
isCompleted: cbk,
todo: _t.todo
}).then(function(data) {
if (data.data.updatedExisting) {
_t.isCompleted = cbk;
} else {
alert('Oops something went wrong!');
}
});
};
// Update the edited Todo
$scope.edit = function($event, i) {
if ($event.which == 13 && $event.target.value.trim()) {
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _t._id,
todo: $event.target.value.trim(),
isCompleted: _t.isCompleted
}).then(function(data) {
if (data.data.updatedExisting) {
_t.todo = $event.target.value.trim();
$scope.isEditable[i] = false;
} else {
alert('Oops something went wrong!');
}
});
}
};
// Delete a Todo
$scope.delete = function(i) {
todosFactory.deleteTodo($scope.todos[i]._id).then(function(data) {
if (data.data) {
$scope.todos.splice(i, 1);
}
});
};
});
todoApp.controller('TodoCtrl', function($rootScope, $scope, todosFactory) {
$scope.todos = [];
$scope.isEditable = [];
// get all Todos on Load
todosFactory.getTodos().then(function(data) {
$scope.todos = data.data;
});
// Save a Todo to the server
$scope.save = function($event) {
if ($event.which == 13 && $scope.todoInput) {
todosFactory.saveTodo({
"todo": $scope.todoInput,
"isCompleted": false
}).then(function(data) {
$scope.todos.push(data.data);
});
$scope.todoInput = '';
}
};
//update the status of the Todo
$scope.updateStatus = function($event, _id, i) {
var cbk = $event.target.checked;
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _id,
isCompleted: cbk,
todo: _t.todo
}).then(function(data) {
if (data.data.updatedExisting) {
_t.isCompleted = cbk;
} else {
alert('Oops something went wrong!');
}
});
};
// Update the edited Todo
$scope.edit = function($event, i) {
if ($event.which == 13 && $event.target.value.trim()) {
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _t._id,
todo: $event.target.value.trim(),
isCompleted: _t.isCompleted
}).then(function(data) {
if (data.data.updatedExisting) {
_t.todo = $event.target.value.trim();
$scope.isEditable[i] = false;
} else {
alert('Oops something went wrong!');
}
});
}
};
// Delete a Todo
$scope.delete = function(i) {
todosFactory.deleteTodo($scope.todos[i]._id).then(function(data) {
if (data.data) {
$scope.todos.splice(i, 1);
}
});
};
});
Just is case the error is in either my factory.js code or html, I will include both.
Here is the factory.js code:
todoApp.factory('todosFactory', function($http){
var urlBase = '/api/todos';
var _todoService = {};
_todoService.getTodos = function(){
return $http.get(urlBase);
};
_todoService.saveTodo = function(todo){
return $http.post(urlBase, todo);
};
_todoService.updateTodo = function(todo) {
return $http.put(urlBase, todo);
};
_todoService.deleteTodo = function(id){
return $http.delete(urlBase + '/' + id);
};
return _todoService;
});
Here the html partial that uses the controller and factory:
<div class="container" ng-controller="TodoCtrl">
<div class="row col-md-12">
<div>
<input type="text" class="form-control input-lg" placeholder="Enter a todo" ng-keypress="save($event)" ng-model="todoInput">
</div>
</div>
<div class="row col-md-12 todos">
<div class="alert alert-info text-center" ng-hide="todos.length > 0">
<h3>Nothing Yet!</h3>
</div>
<div ng-repeat="todo in todos" class=" col-md-12 col-sm-12 col-xs-12" ng-class="todo.isCompleted ? 'strike' : ''">
<div class="col-md-1 col-sm-1 col-xs-1">
<input type="checkbox" ng-checked="todo.isCompleted" ng-click="updateStatus($event, todo._id, $index)">
</div>
<div class="col-md-8 col-sm-8 col-xs-8">
<span ng-show="!isEditable[$index]">{{todo.todo}}</span>
<input ng-show="isEditable[$index]" type="text" value="{{todo.todo}}" ng-keypress="edit($event)">
<input ng-show="isEditable[$index]" type="button" class="btn btn-warning" value="Cancel" ng-click="isEditable[$index] = false" />
</div>
<div class="col-md-3 col-sm-3 col-xs-3" >
<input type="button" class="btn btn-info" ng-disabled="todo.isCompleted" class="pull-right" value="edit" ng-click="isEditable[$index] = true" />
<input type="button" class="btn btn-danger" class="pull-right" value="Delete" ng- click="delete($index)" />
</div>
</div>
</div>
This line must be the cause of the issue:
<input ng-show="isEditable[$index]" type="text" value="{{todo.todo}}"
ng-keypress="edit($event)">
You forgot to pass the $index as the second parameter of the edit function. This should fix it:
<input ng-show="isEditable[$index]" type="text" value="{{todo.todo}}"
ng-keypress="edit($event, $index)">

Categories

Resources