Adding to document array in Mongo from Angular Controller - javascript

I am having an issue inserting a new JSON object into an array of JSON objects in MongoDB from my Angular Controller.
A simple concept of what I am trying to do is for this schema:
var ThingSchema = new mongoose.Schema({
id: Number,
items: [{
item_id: Number,
item_name: String,
item_price: Number
}]
});
And to add it to my mongodb in a the Mongo console I can use:
db.things.update(
{ "_id": ObjectId("579b7860b168c80c1fe8a32a")},
{ $push: {
"items": {
"item_id" : 134,
"item_name" : "sampleItem",
"item_price" : 234.00
}}
})
However, I'm not sure how I can translate that over to an http request from AngularJS. I used Yeoman to scaffold my app and am more interested in getting a functional prototype right now. In my Angular Controller, I am using this function
addNewItem(thing, newItem) {
this.$http.put('/api/things/' + thing._id, { 'items': newItem})
// on success clear the fields
.then( response => {
console.log(response);
alert(this.newItem);
thing.items.push(newItem);
newItem = {};
});
}
When I call this function I add it to my array that I have instantiated, but I cannot access the actual MongoDB even though a 200 response code is returned.
And in my HTML file I have
<form novalidate class="condition-form">
<fieldset>
<input type="number" ng-model="newItem.item_id" name="" placeholder="Enter item id">
<input type="text" ng-model="newItem.item_name" name="" placeholder="Enter item name">
<input type="number" ng-model="newItem.item_price" name="" placeholder="Enter item price">
<input type="submit" ng-click="$ctrl.addNewItem(thing, newItem)" value="Add">
</fieldset>
</form>
I'm really at a loss for how I can translate this mongodb call to my MEAN stack application. If it helps I am using Babel with EMCAScript 6. Any help means a lot!
Thanks

At the backend of it,when the control reaches the things function in API there you will have to use a mongoDB driver(like Mongodb,Mongoose) for interaction with mongo shell. The save function if you are using mongoose will look something like this:
Things.update({'id':req.params.thing._id},{ $push : {items : req.body.items }},function (err,updated) {
if(err){
console.log(err);
} else {
output = {'msg':'Updated Successfully'};
}
return res.json(output);
});

Related

I've got a weird behaviour on req.body on put request on a specific field

I've created a form to save a new real estate to MongoDB, using multer for multi-part form data and body-parser.
Everything works fine except the "rooms" in the "put" (EDIT) route, which are returned as an array, instead of a single value, which makes absolutely no sense, as everything else works just fine.
I've tried to translate the data from the array to a single numerical value, but that does not work as if I try to edit the document once more the new value is pushed in the array instead of replacing the old one.
//EDIT PUT ROUTE
router.put("/immobili/:_id/edit",
upload.fields([{ name: 'estateImages', maxCount: 30 },
{ name: 'document', maxCount: 1 }]),
(req, res) => {
// IMAGES LOGIC SKIPPED HERE AS NOT PERTINENT
Estate.findOneAndUpdate({ _id: requestedId }, {
rooms: req.body.rooms, //BUGGED
squareFeets: req.body.squareFeets, //WORKS CORRECTLY
//CALLBACK ...
}}
// result of console.log of req.body.rooms and req.body.squareFeets
rooms: [ '2', '' ],
squareFeets: 120
//EJS FORM DATA
<form action="/estates/<%= estate._id %>/edit?_method=PUT" method="POST"
enctype="multipart/form-data">
//INPUT NOT WORKING:
<input name="rooms" value="<%= estate.rooms %>"></input>
//INPUT THAT WORKS:
<input name="superficie" value="<%= estate.squareFeets %>"></input>
<input type="submit" value="Publish">
</form>
When you get an array, is because you have multiple inputs with that name:
<input name="rooms" value="<%= estate.rooms %>"></input>
<input name="rooms" value="other-value"></input>
So if you don't want an array, change the input name to the second one.
Omg I have been SO stupid, I've put two inputs with the same name value in the form, thus those values were being saved as an array.
I'm so sorry about this. Thanks for your time!

Submit Javascript Object with Array of Object inside to the API

Good day everyone,
I need a help on how to submit an object with array of object inside and the array of object should be from a checkboxes I've made.
This is my sample checkbox
<input type="checkbox" name="user.preferences" value="Hard" />
<input type="checkbox" name="user.preferences" value="Soft" />
<input type="checkbox" name="user.preferences" value="Small" />
My data javascript looks like this:
user:{
preferences: []
}
When I alert theuser using JSON.stringify, I can able to see a result something like this.
{"preferences": ["Soft","Small"]}
But the problem is, the api that I'm using needs a format like this:
{
"preferences": [
{
"preference": "Hard"
},
{
"preference": "Soft"
},
// so on and so forth
]
}
Please somebody help me. Thank you
You should .map each string in preferences to an object with that preference as its key/value:
const user = {
"preferences": ["Soft", "Small"]
};
user.preferences = user.preferences
.map(preference => ({ preference }));
console.log(user);

Vue.js - Displaying JSON results not working correctly

Before I start this question I am 100% new to vue.js so I may be missing something simple. I've looked through the documentation endlessly and still haven't got a soloution to my problem.
I'm just building a very simple example as practice, where I fill a form and it saves to my DB, but also loads the records I've saved automatically.
This is where it gets strange, when I plug into https://jsonplaceholder.typicode.com/users the JSON data displays correctly in my app.
When I pug into my own backend code, valid JSON is returned but it doesn't display correctly.
This is where I call my Data:
created: function(){
this.$http.get('https://jsonplaceholder.typicode.com/users') // JSON service that is working
this.$http.get('http://localhost:8888/vue_testing/users/get/') // My JSON Service that isn't
.then(function(response){
console.log(response.data);
this.users = response.data;
});
}
Note I am getting back valid JSON from both services.
My valid JSON: http://take.ms/lIa3u
But displays like this: http://take.ms/6dOEL
jsonplaceholder Valid JSON: http://take.ms/VCwKZ
And displays like this:http://take.ms/Ne3fw
This is my complete component code:
<template>
<div class="users">
<h1>Users</h1>
<form v-on:submit="add_user">
<input type="text" v-model="new_user.name" placeholder="Enter name" /><br />
<input type="text" v-model="new_user.email" placeholder="Enter email" />
<input type="submit" value="submit">
</form>
<ul>
<li v-for="user in users">
{{user.name}}: {{user.email}}
</li>
</ul>
</div>
</template>
<script>
export default{
name: 'users',
//Props can accept values from outside the component
data(){
return{
new_user: {},
users: []
}
},
methods:{
add_user: function(e){
//console.log('Add');
this.$http.post('http://localhost:8888/vue_testing/users/set/', this.new_user)
.then(response => {
console.log(response);
}, error => {
console.log(error);
});
e.preventDefault();
}
},
created: function(){
//this.$http.get('https://jsonplaceholder.typicode.com/users')
this.$http.get('http://localhost:8888/vue_testing/users/get/')
.then(function(response){
console.log(response.data);
this.users = response.data;
});
}
}
</script>
<style scoped>
</style>
Again I'm totally new to vue.js, any help in solving this is appricieated. Thanks.
While jsonplaceholder is sending you an array of objects:
[
{
"id": 1,
"name": "Leanne Grehem",
...
}
]
You are sending from your backend an object which holds first the columns, and second a two dimensional array for the data:
{
"COLUMNS": ["ID", "NAME", ...],
"DATA": [
[1, "Leanne Grehem, ...],
[2, "John Doe, ...],
]
}
I would advise you to change your backend so your response looks like that of the jsonplaceholder. Otherwise you would have to make objects out of arrays. Like below:
created: function(){
//this.$http.get('https://jsonplaceholder.typicode.com/users')
this.$http.get('http://localhost:8888/vue_testing/users/get/')
.then(function(response){
console.log(response.data);
let users = [];
const responseData = response.data['DATA']; // Get DATA key from your response
for (user of responseData) { // iterate
users.push( { id: user[0], name: user[1] } ); // create object out of array
}
this.users = users;
});
}
Edit: typo

Node.js: How to write input values of a formular into a JSON file when the submit button is pressed?

NEW: MORE SPECIFIC DESCRIPTION
I'm programming a website with different products of different types. Each product has a detail page with a comment function. The path of a products detail site looks like http://localhost:3000/details/type/name. When a user fills the form to write a comment and presses the submit button, all data should be append to the product type's JSON file which looks like:
type1.json
[
{
"name": "Product1",
"description": "Description1",
"comments":[
{
"user": "Matthew",
"message": "Very Good!",
"timestamp": "2017-03-17T17:51Z"
},{
"user": "Lea",
"message": "Nice",
"timestamp": "2017-03-10T13:29Z"
}
]
},
{
"name": "Product2",
"description": "Description2",
"comments":[
{
"user": "Ann",
"message": "This is very useful!",
"timestamp": "2017-02-02T19:30Z"
},{
"user": "Tim",
"message": "Awesome",
"timestamp": "2017-04-01T20:25Z"
}
]
]
This is the part of my HTML file which contains the form:
details.html
<form action="" method="POST" id="commentForm">
<div class="form-group">
<input type="text" id="name"
placeholder="Name" class="form-control" name="name"/>
</div>
<div class="form-group">
<textarea cols="30" rows="5" class="form-control" id="message" placeholder="Message" name="message"></textarea>
</div>
<div class="form-group">
<div class="col-md-6 text-center">
<button type="reset" class="btn btn-default">
<span class="glyphicon glyphicon-remove"></span>
Reset
</button>
</div>
<div class="col-md-6 text-center">
<button type="submit" class="btn btn-success">
<span class="glyphicon glyphicon-ok"></span>
Send
</button>
</div>
</div>
</form>
This is the relevant part of my JavaScript file:
details.js
$(function () {
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
$("#commentForm").bind("submit", function(evt) {
console.log(JSON.stringify($("#commentForm").serializeObject()));
$.ajax({
url: window.location.pathname,
type: "POST",
contentType: "application/json",
data: JSON.stringify($("#commentForm").serializeObject()),
success: function(data) {
console.log('success');
console.log(JSON.stringify(data));
},
});
return false;
});
});
Now, the question is: What to write in the app.js?
In the following is an excerpt of the current app.js.
app.js
const express = require("express");
const app = express();
const fs = require('fs');
const path = require("path");
const jsonfile = require('jsonfile');
const bodyParser = require('body-parser');
app.use(bodyParser.json());
const type1File = __dirname + "/data/type1.json";
...
app.post("/details/:typ/:name", function (req, res) {
if (req.params.typ == "type1") {
const apps = readJsonFile(type1File);
res.send(getProduct(apps, req));
???What to write here???
}
...
});
function readJsonFile(file) {
try {
const data = fs.readFileSync(file);
return JSON.parse(data);
} catch (e) {
console.log(file + "could not be read. " + e);
return [];
}
}
What should I add? How is it possible that the data will be written to the "comment"-key of the right JSON object? Please help me, I spent a lot of time trying out different things, but nothing works right.
Note: This answer was written before the question was rewritten:
https://stackoverflow.com/posts/43213085/revisions
This question is so general that it's hard to give you any specific answer. But if you want persistent data in your app then you should use a database.
Some databases like Mongo, Postgres or Redis need to be run as standalone application either on the same or on a different server. Some embedded databases like SQLite don't need a standalone process and can be run directly in your application. There are multiple choices of databases and it's something that you have to choose yourself for your particular case. But you should choose some database to store the data.
It's not that it is impossible to write to JSON files on updates and then read those files as needed, but the amount of work that you'd have to do to synchronize the access to the data so that no two requests to write happen at the same time and no read happens while a write is in progress, all that without accidentally blocking the event loop in the process and handling multiple requests concurrently, is something greatly more difficult than just using any database as intended.
Some databases like Mongo will let you store any JSON documents (actually it stores BSON but for the user it's just like JSON). Using a document database like Mongo or CouchDB will be most similar to having JSON files but using a relational database could work as well. Pretty much every persistent data is kept in databases. If you can write your own database that stores data in JSON files then by all means do it if you need, but if you can't then just use the right tool for the job.
That having been said, if you still insist on reading and writing JSON files, here's what to do:
To write data as JSON into files, you will need to use JSON.stringify() and fs.writeFile().
To read JSON data from files, you will need to use fs.readFile() and JSON.parse().
Things to keep in mind:
JSON.parse() and JSON.stringify() must always be wrapped in a try/catch block or otherwise your app will crash (or use tryjson module from npm).
Never use methods of the fs modules with "Sync" in their name or otherwise your app will be blocked from serving requests.
You need to implement locking and synchronization of the access to files or otherwise your data will get corrupted - this is the most difficult and most important part, and this is the reason why people use databases for that sort of things.

mean.io $save() returning 404 file not found

I am using mean.io and have come across an issue with a database collection that I don't understand. I have a couple of lists that are similar to this and they work fine. When I try to add an item to the Materials list, I get a 404 response from the server side and I have no idea why.
So... Let me start with a little bit of a code dump.
Here is the Model file entries
var UnitSchema = new Schema({
unit: {
type: String,
required: true
}});
and...
var MaterialSchema = new Schema({
name: {
type: String,
required: true
},
unit: {
type: mongoose.Schema.ObjectId,
ref: 'Unit'
},
delivery_offset: {
type: String,
required: true
}});
So I have two basic shemas. One is Units and the other is Materials which contains a reference to the Units collection.
This is the route file for the materials.
module.exports = function(Materials, app, auth, database) {
//Setting up the materials api
app.get('/materials', auth.requiresAdmin, materials.all);
app.post('/materials', auth.requiresAdmin, materials.create);
app.get('/materials/:materialId', auth.requiresAdmin, materials.show);
app.put('/materials/:materialId', auth.requiresAdmin, materials.update);
app.delete('/materials/:materialId', auth.requiresAdmin, materials.destroy);
app.param('materialId', materials.material);
};
I suppose the next logical entry would be the server side controller's create function.
exports.create = function(req, res, next, id) {
var material = new Material(req.body);
material.save(function(err) {
if (err) {
return res.status(500).json({
error: 'Cannot save the material of measure'
});
}
res.json(material);
});
};
So that's it for the server side stuff. Now here is the client side. Before I post, I do have a service setup for the PUT operation but this is a POST so...there's that. Here is the client side controller.
This first snippet get's called when the webpage loads. It basically sets up the $scope variables and the ngTable.
$scope.init = function() {
Materials.query({}, function(materials) {
$scope.materials = materials;
$scope.units = Units.query();
var data = materials;
$scope.tableMaterialsParams = new NGTableParams({
page: 1,
count: 10
},{
total: data.length,
getData: function($defer, params) {
params.total(data.length);
var orderedData = params.sorting()?$filter('orderBy')(data, params.orderBy()):data;
$defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
});
};
This next snippet is the add function in the client side controller that gets called when the user wants to add a new material to the list.
$scope.addMaterial = function() {
if (!$scope.materials) $scope.materials = [];
var material = new Materials({
name: $scope.name,
unit: $scope.unit._id,
delivery_offset: $scope.delivery_offset
});
material.$save(function(response) {
$scope.materials.push(response);
var data = $scope.materials;
$scope.tableMaterialsParams.total(data.length);
$scope.tableMaterialsParams.reload();
});
this.name = this.unit = this.delivery_offset = '';
};
I am using ngTable for the frontend display so that is why you see some of those calls to tableBlahBlah. It's there to update and refresh the table after the save. Here is the form's html.
<form id="material-add-form" class="form-group" name="addMaterialForm" data-ng-submit="addMaterial()">
<div class="row">
<span class="col-sm-5">
<input type="text" data-ng-model="name" placeholder="Material Name" class="form-control m-b"/>
</span>
<span class="col-sm-2">
<select ng-model="unit" class="form-control m-b"
ng-options="obj.unit for obj in units track by obj._id">
<option value="" disabled selected>Unit of Measure</option>
</select>
</span>
<span class="col-sm-2">
<select data-ng-model="delivery_offset" class="form-control m-b">
<option value="" disabled selected>Delivery Offset</option>
<option value="Two Days Prior">Two Days Prior</option>
<option value="One Day Prior">One Day Prior</option>
<option value="Same Day">Same Day</option>
</select>
</span>
<button class="btn btn-primary " type="submit">
<i class="fa fa-check"></i>
Add
</button>
</div>
</form>
Any help or ideas as to what i'm doing wrong would be greatly appreciated. As I've stated before, I have other lists that are very similar that work just find.
Found the answer to the problem. It was a mistake on my part which I figured. Just not sure exactly where. It was in the server side controller.
exports.create = function(req, res, next, id)
I am still relatively new to mean stack development so i'm not sure why this is wrong. I changed the function definition to
exports.create = function(req, res, next)
and it works!
There was also a second problem with my implementation. That was not in the question since I was never able to get it working with the 404 error so I never saw it. When I saved the material item to the database, the unit (ObjectId) would not populate. It would be blank. I changed the save() call to this
material.save(function(err) {
if (err) {
return res.json(500, {
error: 'Cannot save the material'
});
} else {
material.populate({
path: 'unit',
select: 'unit'
}, function(err, doc) {
res.json(doc);
});
}
});
And now it works as intended. The resulting database entry looks something like
{ "_id" : ObjectId("5524547488842d6cb408c003"), "name" : "TESTING", "unit" : ObjectId("550f7047e9cc68da538fe46f"), "delivery_offset" : "Same Day" }
I was able to find and fix the problems but I'm a little unclear as to why it worked.

Categories

Resources