Index page in node app only routing to first show page - node.js

I'm building a goals app with node/express/psql. When I click on a goal on the index page, I want it to link to the show page for that goal. However, each one is linking to the goal with the id=1. I'm confused because the .forEach I use to print the objects in the first place is working, just not the link. Any help would be appreciated!
index.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="styles.css">
<meta charset="UTF-8">
<title>Goals</title>
</head>
<body>
<h1>Here's a look at all of your goals!!</h1>
New Goal?
<% goals.forEach(goal => { %>
<div class="goal-container">
<h2><%= goal.description %></h2>
<p><%= goal.step1 %></p>
<p><%= goal.step2 %></p>
<p><%= goal.step3 %></p>
<p><%= goal.step4 %></p>
</div>
<% }) %>
</body>
</html>
controller:
const Goal = require('../models/goal');
const goalsController = {};
goalsController.index = (req, res) => {
Goal.findAll(req.user.id)
.then(goal => {
res.render('goals/', { goals: goal });
})
.catch(err => {
res.status(400).json(err);
});
};
goalsController.show = (req, res) => {
Goal.findById(req.params.id)
.then(goals => {
console.log('goals show');
res.render(`goals/show`, { goals:goals })
})
.catch(err => {
res.status(400).json(err);
});
};
module.exports = goalsController;
routes:
const express = require('express');
const goalsController = require('../controllers/goals-controllers');
const goalsRouter = express.Router();
goalsRouter.get('/', goalsController.index);
goalsRouter.get('/new', goalsController.new);
goalsRouter.get('/:id', goalsController.show);
goalsRouter.get('/:id/edit', goalsController.edit);
goalsRouter.put('/:id', goalsController.update);
goalsRouter.post('/', goalsController.create);
goalsRouter.delete('/:id', goalsController.delete);
module.exports = goalsRouter;
model:
const db = require('../db/config');
const Goal = {};
Goal.findAll = id => {
return db.query(`SELECT * FROM goals JOIN users ON goals.user_id = users.id WHERE goals.user_id = $1`, id)
};
Goal.findById = id => {
console.log('findbyId')
return db.oneOrNone(`SELECT * FROM goals WHERE id = $1`, [id])
};
module.exports = Goal;
Thanks in advance!

I believe there is something wrong with your index.ejs
Try replacing <h2><%= goal.description %></h2>
with <h2><%= goal.description %></h2>

Related

Getting a 404 error on my nodejs webpage (an example webpage being built following a udemy course)

Back again with another development error. This time around i'm getting a 404 error on one of the routes in my nodejs webpage. I've checked for syntax errors everywhere and apparently have none (if you guys spot something I overlooked please do tell me). The routes are registered correctly (at least as far as my understanding of routes in expressjs goes) and the link that should take me to the page (the "a" tag with a class of "btn" in the products.ejs file, line 23) that isn't loading has a correctly spelled route, because of this I am completely bamboozled as to what it could be and need your help!
I'll embed the code I currently have below so you guys can take a look, and if it's not too much to ask please explain to me what went wrong this time.
Thanks to everyone!
p.s.
My terminal isn't throwing any errors from what I could see so I couldn't attach any!
edit-product.ejs file (view):
<%- include('../includes/head.ejs') %>
<link rel="stylesheet" href="/css/forms.css">
<link rel="stylesheet" href="/css/products.css">
</head>
<body>
<%- include('../includes/navigation.ejs') %>
<main>
<form class="product-form" action="/admin/add-product" method="POST">
<div class="form-control">
<label for="title">Title</label>
<input type="text" name="title" id="title">
</div>
<div class="form-control">
<label for="imageUrl">Image Url</label>
<input type="text" name="imageUrl" id="imageUrl">
</div>
<div class="form-control">
<label for="price">Price</label>
<input type="number" name="price" id="price" step="0.01">
</div>
<div class="form-control">
<label for="description">Description</label>
<textarea name="description" id="description" rows="5"></textarea>
</div>
<button class="btn" type="submit"><% if (editing) { %>Update product<% } else { %>Add Product<% } %></button>
</form>
</main>
<%- include('../includes/end.ejs') %>
product.ejs file (view):
<%- include('../includes/head.ejs') %>
<link rel="stylesheet" href="/css/products.css">
</head>
<body>
<%- include('../includes/navigation.ejs') %>
<main>
<% if (prods.length > 0) {%>
<div class="grid">
<div class="card">
<% for (let product of prods) { %>
<article class="product-item">
<header class="card__header">
<h1 class="product__title"> <%= product.title %> </h1>
</header>
<div class="card__image">
<img src="<%= product.imageUrl %>", alt="">
</div>
<div class="card__content">
<h2 class="product__price"> $<%= product.price %> </h2>
<p class="product__description"> <%= product.description %> </p>
</div>
<div class="card__actions">
Edit
<form action="/admin/delete-product" method="POST">
<button class="btn" type="submit"> Delete </button>
</form>
</div>
</article>
<% } %>
</div>
</div>
<% } else { %>
<h1>No Products</h1>
<% } %>
</main>
<%- include('../includes/end.ejs') %>
admin.js file (routes):
const path = require('path');
const express = require('express');
const adminController = require('../controllers/admin');
const router = express.Router();
// /admin/add-product => GET
router.get('/add-product', adminController.getAddProduct);
// /admin/product => GET
router.get('/products',adminController.getProducts);
// /admin/add-product => POST
router.post('/add-product', adminController.postAddProduct);
router.get('/edit-product/:productId', adminController.getEditProduct);
module.exports = router;
product.js file:
const fs = require('fs');
const path = require('path');
const p = path.join(
path.dirname(process.mainModule.filename),
'data',
'products.json'
);
const getProductsFromFile = cb =>{
fs.readFile(p, (err, fileContent) => {
if(err) {
cb([]);
} else{
cb(JSON.parse(fileContent));
}
});
};
module.exports = class Product {
constructor(title, imageUrl, description, price) {
this.title = title;
this.imageUrl = imageUrl;
this.description = description;
this.price = price;
}
save() {
this.id = Math.random().toString();
getProductsFromFile(products => {
products.push(this);
fs.writeFile(p, JSON.stringify(products), (err) => {
console.log(err);
});
});
}
static fetchAll(cb) {
getProductsFromFile(cb);
};
static findById(id, cb) {
getProductsFromFile(products => {
const product = products.find(p => p.id === id);
cb(product);
});
};
};
admin.js file (controller):
const Product = require('../models/product');
exports.getAddProduct = (req, res, next) => {
res.render('admin/edit-product', {
pageTitle: 'Add Product',
path: '/admin/add-product',
editing: false
});
};
exports.postAddProduct = (req, res, next) => {
const title = req.body.title;
const imageUrl = req.body.imageUrl;
const price = req.body.price;
const description = req.body.description;
const product = new Product(title, imageUrl, description, price);
product.save();
res.redirect('/');
};
exports.getEditProduct = (req, res, next) => {
const editMode = req.query.edit;
if (!editMode) {
return res.redirect('/');
}
const prodId = req.params.productId;
Product.findById(prodId, product => {
if(!product) {
return res.redirect('/');
}
res.render('admin/edit-product', {
pageTitle: 'Edit Product',
path: '/admin/edit-product',
editing: editMode,
product: product
});
});
};
exports.getProducts = (req, res, next) => {
Product.fetchAll(products => {
res.render('admin/products', {
prods: products,
pageTitle: 'Admin Products',
path: 'admin/products'
});
});
};
Upon looking yet again at the videos I found the answer to my problem. Everything in the code is written correctly, the problem relies on the url I was using. The problematic route was "/admin/edit-product" because there isn't a route connecting to that view, instead the route in the code is "/edit-product/:productId". My mistake was that I had to specify manually the url in my browser because I haven't coded it otherwise.

Axios GET request got error, but I see response in browser

Trying to make VUE application with backend on Node.JS. But the simplest code doesn't work.
My backend:
const HTTPServer = require("http")
server = HTTPServer.createServer((req, res) => {
console.log(req.url)
console.log(req.method)
res.write('Hello world!')
res.end()
})
server.listen(3000, () => {
console.log('Server is up')
})
My frontend:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://unpkg.com/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/axios#0.12.0/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<title>Get test</title>
</head>
<body>
<div id="app">
<div>
<button class="btn" #click="btn_pressed">Send GET request</button>
</div>
<div> {{comment}}</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
comment: ''
},
methods: {
btn_pressed: function () {
axios
.get('http://localhost:3000')
.then(response => {
this.comment = 'Response is ' + response.data
})
.catch(error => {
this.comment = 'The error is ' + error
})
}
}
})
</script>
</body>
</html>
I see the correct response in Network activity in Chrome, but get "Network error" in the code.
You can tried something just like this:
<script>
var app = new Vue({
el: '#app',
data: {
comment: ''
},
mounted() {
this.btn_pressed()
},
methods: {
btn_pressed() {
axios.get('/')
.then(res => {
this.comment = 'Response is ' + res.data
})
.catch(err => {
this.comment = 'The error is ' + err
})
}
}
})

NodeJS Mongoose insert event to public/index.html

I'm learning nodejs and I have a project where I want users to post form data which then populates an html table located in public/index.html.
At the moment, I am writing the submitted data to a database collection using the following code:
const mongoose = require('mongoose')
const express = require('express');
const app = express();
const server = app.listen(3000);
app.use(express.json()); // for retrieving form data
app.use(express.static('public'));
mongoose.connect('mongodb://localhost/class', {useNewUrlParser: true})
.then( () => console.log('Connected to class database'))
.catch( () => console.error('Connection attempt to class database failed'))
const personSchema = new mongoose.Schema({
name: String,
date: {type: Date, default: Date.now}
})
const Person = mongoose.model('Person', personSchema)
app.post('/join_class', (req,res) => {
res.send('... joining class')
console.debug(req.body.name)
// document.getElementById('class_table').insertRow(req.body.name)
joinClass(req.body)
})
async function joinClass(data){
console.log(data)
person = new Person({
name: data.name
})
await person.save();
}
My problem is I need the same data to populate an HTML table located in my public/index.html but of course I don't have access to the document object in index.js. The index.html file is below:
<!DOCTYPE html>
<html lang="en">
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.dev.js'></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- <script src="/client.js"></script> -->
<title>TestING</title>
</head>
<body>
<table id='class_table'>
<tr><th>Class</th></tr>
<tr><td>testing</td></tr>
</table>
</body>
</html>
So, how can I create a mongoDB event/alert such that when the post data is inserted into the database, the same data is made available to index.html where I CAN use the document object to populate the table?
Here's a example in which you can add new Person's and the list in the index.html page should update on a successful insert.
index.js
app.post('/join_class', (req, res) => {
var person = new Person({
name: req.body.name
});
person.save().then((data) => {
res.send(data);
}).catch((err) => {
res.status(500).send(err);
});
})
app.get('/class', (req, res) => {
Person.find({}).then((data) => {
res.send(data);
}).catch((err) => {
res.status(500).send(err);
});
})
index.html (body tag content)
<body>
<div>
Name:<br>
<input type="text" id="name" value="">
<br>
<button onclick="addPerson()">Add Person</button>
</div>
<br/>
<b>Person's in List: </b>
<ul id='class_table'>
</ul>
<script src="/client.js"></script>
</body>
client.js
function listPerson() {
var req = new XMLHttpRequest();
req.open("GET", '/class');
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
req.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var aList = JSON.parse(req.responseText),
list = document.getElementById("class_table");
list.innerHTML = "";
aList.forEach(e => {
var item = document.createElement("li");
item.innerHTML = e.name;
list.appendChild(item);
});
}
};
req.send();
}
function addPerson() {
var req = new XMLHttpRequest();
req.open("POST", '/join_class');
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
req.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) { listPerson(); } //Get list of Person on completion
};
var sName = document.getElementById("name").value;
req.send(JSON.stringify({ "name": sName }));
}
//Initially load a list of Person's
listPerson();

How to insert and retrieve video from mongodb in node js

I new to mean stack. I want to upload video to mongodb and then I want to retrieve it.
this is app.js file
`const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const crypto = require('crypto');
const mongoose = require('mongoose');
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
const Grid = require('gridfs-stream');
const methodOverride = require('method-override');
const app = express();
// Middleware
app.use(bodyParser.json());
app.use(methodOverride('_method'));
app.set('view engine', 'ejs');
// Mongo URI
const mongoURI = 'mongodb://fawad:Fawad123#ds155243.mlab.com:55243/imageupload';
// Create mongo connection
const conn = mongoose.createConnection(mongoURI);
// Init gfs
let gfs;
conn.once('open', () => {
// Init stream
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('uploads');
});
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
// #route GET /
// #desc Loads form
app.get('/', (req, res) => {
gfs.files.find().toArray((err, files) => {
// Check if files
if (!files || files.length === 0) {
res.render('index', { files: false });
} else {
files.map(file => {
if (
file.contentType === 'video/mp4' ||
file.contentType === 'video/webm '
) {
file.isVideo = true;
} else {
file.isVideo = false;
}
});
res.render('index', { files: files });
}
});
});
// #route POST /upload
// #desc Uploads file to DB
app.post('/upload', upload.single('file'), (req, res) => {
// res.json({ file: req.file });
res.redirect('/');
});
// #route GET /files
// #desc Display all files in JSON
app.get('/files', (req, res) => {
gfs.files.find().toArray((err, files) => {
// Check if files
if (!files || files.length === 0) {
return res.status(404).json({
err: 'No files exist'
});
}
// Files exist
return res.json(files);
});
});
// #route GET /files/:filename
// #desc Display single file object
app.get('/files/:filename', (req, res) => {
gfs.files.findOne({ filename: req.params.filename }, (err, file) => {
// Check if file
if (!file || file.length === 0) {
return res.status(404).json({
err: 'No file exists'
});
}
// File exists
return res.json(file);
});
});
// #route GET /image/:filename
// #desc Display Image
app.get('/video/:filename', (req, res) => {
gfs.files.findOne({ filename: req.params.filename }, (err, file) => {
// Check if file
if (!file || file.length === 0) {
return res.status(404).json({
err: 'No file exists'
});
}
// Check if image
if (file.contentType === 'video/mp4' || file.contentType === 'video/webm') {
// Read output to browser
const readstream = gfs.createReadStream(file.filename);
readstream.pipe(res);
} else {
res.status(404).json({
err: 'Not an image'
});
}
});
});
// #route DELETE /files/:id
// #desc Delete file
app.delete('/files/:id', (req, res) => {
gfs.remove({ _id: req.params.id, root: 'uploads' }, (err, gridStore) => {
if (err) {
return res.status(404).json({ err: err });
}
res.redirect('/');
});
});
const port = 9000;
app.listen(port, () => console.log(`Server started on port ${port}`));
`
index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<style>
video {
width: 100%;
}
</style>
<title>Mongo File Uploads</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 m-auto">
<h1 class="text-center display-4 my-4">Mongo File Uploads</h1>
<form action="/upload" method="POST" enctype="multipart/form-data">
<div class="custom-file mb-3">
<input type="file" name="file" id="file" class="custom-file-input">
<label for="file" class="custom-file-label">Choose File</label>
</div>
<input type="submit" value="Submit" class="btn btn-primary btn-block">
</form>
<hr>
<% if(files){ %>
<% files.forEach(function(file) { %>
<div class="card card-body mb-3">
<% if(file.isVideo) { %>
<video src="video/<%= file.filename %>" alt="">
<% } else { %>
<%= file.filename %>
<% } %>
<form method="POST" action="/files/<%= file._id %>?_method=DELETE">
<button class="btn btn-danger btn-block mt-4">Delete</button>
</form>
</div>
<% }) %>
<% } else { %>
<p>No files to show</p>
<% } %>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
</body>
</html>
this code work for image..when I used (file.contentType = image/png).
but for video it's not working.. is Image and video upload are same?
You can use Formidable. As It support multipart data. And for retrieve video you can use fs(file system)
Actually you don't save media(image or video) in DB, you store it in some storage drive(locally or cloud) like s3 bucket, and then store its location url in your DB.
and this code might be helpful.
saveImage(urlPath, folderPath, multiple) {
return new Promise((resolve, reject) => {
var timestamp = Date.now();
var filepath = urlPath;
var imageUrl;
var ext = path.extname(filepath || '').split('.');
var extension = ext[ext.length - 1];
var tmp_path = filepath;
imageUrl = folderPath + timestamp + '.' + extension;
if (multiple != '' && multiple != undefined) { imageUrl = folderPath + timestamp + multiple + '.' + extension; }
var target_path = __dirname + '/../' + imageUrl;//Change according to your location.
console.log("target_path", target_path);
mv(tmp_path, target_path, { mkdirp: true }, function (err) { })
return resolve({ status: 1, url: imageUrl });
})
}
Now, urlPath is your media path, foldarPath is path where you want to store your media and multiple is to give unique name.
you can call this method like this.
var multiple = Number(0) + Number(10);
console.log("multiple", multiple);
var saveImage = await 'YOUR_FILE_NAME'.saveImage(files.photo[0].path, 'public/Image/', multiple);
console.log(saveImage);
<video src="video/<%= file.filename %>" controls alt="">
//Try adding the controls command in the video tag. It helps the video run.

Use a Checkbox to Show Disabled Users

I'm making a website in which I show enabled users in a Bootstrap by default, but there's a checkbox to show all users. But I'm not sure how I can go about implementing it properly.
I thought about doing:
<form class="" action="/admin" method="get">
<input type="checkbox" name="showDisabled" value="">
<input type="submit" name="" value="">
</form>
And in my /admin route:
if (req.body.showDisabled) {
User.find({}, (err, users) => {
if (err) {
console.log(err);
}
}).then((users) => {
res.render('pages/userTable', {users: users});
})
} else if (!req.body.showDisabled) {
User.find({enabled: true}, (err, users) => {
if (err) {
console.log(err);
}
}).then((users) => {
res.render('pages/userTable', {users: users});
})
}
But that seems really janky. What's a better way to do it?
You can try this:
Router
app.get('/getDiasbleUser', function(req, res) {
var getDiasbleUser = req.params.showDisabled
if (getDiasbleUser == "Yes") {
User.find({}, (err, users) => {
if (err) {
console.log(err);
}
}).then((users) => {
res.send(users);
})
} else if (getDiasbleUser == "No") {
User.find({
enabled: true
}, (err, users) => {
if (err) {
console.log(err);
}
}).then((users) => {
res.send(users);
})
}
})
HTML (Template)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form class="" action="/admin" method="get">
<input type="checkbox" id="disableId" name="showDisabled" value=""><br>
<div id="appendValue"></div>
<br>
<button class="btn btn-primary">Submit</button>
</form>
</div>
</body>
//Ajax call
<script>
$('#disableId').on('click', function () {
$(this).val(this.checked ? 1 : 0);
//console.log($(this).val());
var url = "";
if($(this).val() == 1){
url = "/getDiasbleUser?showDisabled=yes"
} else {
url = "/getDiasbleUser?showDisabled=no"
}
$.ajax({
url: url,
success: function(data){
//Here data will be return result(You will append into div or table)
//like : $('#appendValue').append(data)
}
})
});
</script>
</html>

Resources