I am new to sails.js and when I'm trying to learn it by making a simple web app, I encountered a problem. My app is about class management. My model has 3 table: student, course and list. The student table save information about students, course save information about courses. The list.table save information about which student is in a course and their mark, their absent days in this class. I used res.view() and it worked for any view that need one array. However, when I try to write function which need information from 2 model: student and course, res.view() didnt help (or I dont know to do it properly)
module.exports = {
'new': function(req, res, next) {
Course.find(function foundCourses (err, courses) {
if (err) return next(err);
res.view({courses: courses});
});
/* Student.find(function foundStudents (err, students) {
if (err) return next(err);
res.view({
students: students
});
}); */
},
//more other code here
};
There, I want to send both array to view but I dont know how. Sails.js only let me send one array if I do like that. Please help me . Thank you a lot!
You can't call res.view twice. You need to gather all data first and than send it to view.
2 methods:
module.exports = {
'new': function(req, res, next) {
var foundCourses = function (err, courses) {
if (err) return next(err);
Student.find(function foundStudents (err, students) {
if (err) return next(err);
res.view({
courses: courses,
students: students
});
});
});
Course.find(foundCourses);
},
//more other code here
};
or you can use async.parallel
module.exports = {
'new': function(req, res, next) {
var dataCallback = function (err, data) {
if (err) return next(err);
var courses = data[0];
var students = data[1];
res.view({
courses: courses,
students: students
});
};
async.parallel([
function(callback) {
Course.find(callback);
},
function(callback) {
Student.find(callback);
}
],dataCallback);
},
};
Related
I'm actually creating a chat like Discord with servers and channels using Node.JS, MongoDB and Mongoose.
Actually, my structure is this one:
https://github.com/copostic/My-Simple-Chat/tree/master/models
But to get the conversations, I have to make so much nested functions and I would like to know if there was a better way to organize my code..
Here's the code with the nested functions, I'm trying to get the message list of each channel of each server:
"use strict"
const Server = require('../models/server'),
Channel = require('../models/channel'),
Message = require('../models/message'),
User = require('../models/user');
exports.getChannels = function (req, res, next) {
// Only return one message from each conversation to display as snippet
Server.find({members: req.session._id})
.select('_id')
.exec(function (err, servers) {
if (err) {
res.send({ error: err });
return next(err);
}
servers.forEach(function (server) {
Channel.find({ serverId: server })
.exec(function (err, channels) {
// Set up empty array to hold conversations + most recent message
let fullConversations = [];
channels.forEach(function (channel) {
Message.find({
'channelId': channel._id
})
.sort('creationDate')
.limit(1)
.populate({
path: "author",
select: "profile.firstName profile.lastName"
});
.exec(function (err, message) {
if (err) {
res.send({
error: err
});
return next(err);
}
fullConversations.push(message);
if (fullConversations.length === conversations.length) {
return res.status(200).json({
conversations: fullConversations
});
}
});
});
});
});
});
};
Thanks a lot
I have this snippet of code in my controller but I'd like to filter the incoming records by a field archived == true in the MongoDB document.
Here is the filterless code that works right now. I'm not sure where to add the filter. I tried adding a simple filter() using prototype but it broke. What am I missing?
var Candidate = require('../models/candidate');
var async = require('async');
// Display list of all Candidate
exports.candidate_list = function(req, res, next) {
Candidate.find({}) //should something go in between the {}?
.sort([['name', 'ascending']])
.exec(function (err, list_candidates) {
if (err) { return next(err); }
//Successful, so render
res.render('candidate_list', { title: 'Candidates', list_candidates: list_candidates});
});
};
You can try this
var Candidate = require('../models/candidate');
var async = require('async');
// Display list of all Candidate
exports.candidate_list = function(req, res, next) {
Candidate.find({achived:true})
.sort([['name', 'ascending']])
.exec(function (err, list_candidates) {
if (err) { return next(err); }
//Successful, so render
res.render('candidate_list', { title: 'Candidates', list_candidates: list_candidates});
});
};
Suppose each users has huge amount of data that no need to store those in single table. I want to store each user's data in separate database for faster query.
I want when any user logging in loopback, change datasets based on users connection strings that stored in User model.
I read all of loopback docs and try so many practice to do this but i can't implement this.
I try this in server/server.js:
app.use(loopback.context());
app.use(loopback.token());
app.use(function (req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.User.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
console.log('server.js');
var loopbackContext = loopback.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
var DataSource = require('loopback-datasource-juggler').DataSource;
var ds = new DataSource('memory');
app.datasources.db= ds;// <----- IT DOES'NT WORKING
next();
});
});
but IT DOES'NT WORKING (marked in code).
Any idea to solve this issue?
You can use attachTo() function to attach a datasource based on conditions.
app.use(loopback.context());
app.use(loopback.token());
app.use(function (req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.User.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
//console.log('server.js');
var loopbackContext = loopback.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
// This way you can attach memory datasource to model.
// This only covers models defined using app.model();
var models = app.models();
models.foreach(function(model) {
model.attachTo(app.dataSources.memory);
});
next();
});
});
Also use defined datasource, memory in datasource.json.
{
"memory": {
"name": "memory",
"connector": "memory"
}
}
Please refer this too: app.models()
I am new with node.js, mongoDB and jade.
To redirect to userlist page I have following route in /routes/index.js
router.get('/userlist', function(req, res) {
var db = req.db;
var collection = db.get('usercollection');
collection.find({},{},function(e,docs){
res.render('userlist', {
"userlist" : docs
});
});
});
This route redirects me to userlist page. There I display a list of all users. Here I have a link on each record to view details:
following is my userlist.jade
extends layout
block content
h1.
User List
ul
each user, i in userlist
li
a(href="/viewuser/id/#{user._id}")=user.username
This:
a(href="/viewuser/id/#{user._id}")=user.username
Gives me:
Dhara
Now I don't know what route should be there for view details on click of the link and how to get selected record data for view screen.
I use a clear and distributed stack MEAN.js, with a yeoman constructor that will help you to build secure and good-practice programming applications.
This is the way I get a concrete user data. It´s a little more spread than your code but it´s clear.
Hope it helps!
routes.js
app.use('/api/users', require('./api/user'));
api/user/index.js
var controller = require('./user.controller');
router.get('users/:id', controller.show);
user.controller.js:
// Get a single user
exports.show = function(req, res) {
User.findById(req.params.id, function (err, user) {
if(err) { return handleError(res, err); }
if(!user) { return res.send(404); }
return res.json(user);
});
};
then I´ll call for a user with an url like users/xxxxxx where xxxx is the user id. Then if you want to do it like /viewuser/id/xxxxxx you will need to change the route like this:
api/user/index.js
var controller = require('./user.controller');
router.get('viewuser/id/:id', controller.show);
user.controller.js:
// Get a single user
exports.show = function(req, res) {
User.findById(req.params.id, function (err, user) {
if(err) { return handleError(res, err); }
if(!user) { return res.send(404); }
return res.json(user);
});
};
maybe you need to specify the view in your app server file
app.get('/userlist', function(req, res){
res.render('userlist', {
title: 'Your_Title'
});
});
I hope to be helpful!
I'm trying to create an object in Angular.
Here is my factory
angular.module('myApp')
.factory('Order', function($resource) {
return $resource('/api/products?id=:id/orders?id=:orderId', { id: "#_id" }, {
query: { method: 'GET', isArray: false },
update: { method: 'PUT'}
});
});
Here is my controller
angular.module('myApp').controller('OrderCtrl',
function ($scope, $rootScope, $stateParams, Order, Auth) {
$scope.myOrder = Order.get({"id": 1, "orderId": 1});
}
});
Here is my routes.js file in my server directory
app.use('/api/products', require('./api/product'));
In my api/products directory, I have an index file
var express = require('express');
var controller = require('./product.controller');
var router = express.Router();
// Endpoints for products
router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
// Endpoints for product - order definitions
router.get('/:id/orderDefinitions', controller.showOrderDefinitions);
router.post('/:id/orderDefinitions', controller.addOrderDefinitions);
// Endpoints for product - orders
router.get('/:id/orders', controller.showProductOrders);
router.get('/:id/orders/:orderId', controller.showIndividualProductOrder);
router.put('/:id/orders/:orderId', controller.updateIndividualProductOrder);
router.delete('/:id/orders/:orderId', controller.deleteOrder);
module.exports = router;
Here are the two endpoints I'm trying to access
router.get('/:id/orders/:orderId', controller.showIndividualProductOrder);
router.put('/:id/orders/:orderId', controller.updateIndividualProductOrder);
Here is my controller
exports.showIndividualProductOrder = function(req, res) {
Product.findById(req.params.id, function (err, product) {
if(err) { return handleError(res, err); }
if(!product) { return res.send(404); }
Order.find({productId: product._id, _id: req.params.orderId}, function (err, order) {
console.log("found individual product order", order);
if(err) { return handleError(res, err); }
if(!order) { return res.send(404); }
// Get product's order
return res.json(order[0]);
});
});
};
// Update an individual order for a product
exports.updateIndividualProductOrder = function(req, res) {
Order.find({productId: product._id, _id: req.params.orderId}, function (err, order) {
if(err) { return handleError(res, err); }
if(!order) { return res.send(404); }
var updatedOrder = _.merge(order, req.body);
console.log("updating order", order, updatedOrder);
updatedOrder.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, product);
});
});
};
And yet, I still get this error
Error: [$resource:badcfg] Error in resource configuration for action get. Expected response to contain an object but got an array (Request: GET /api/products?id=1/orders?id=1)
I used Postman to test the API endpoints, and the JSON output is an array. But in my factory, I clearly stated that I didn't want an array with the isArray option.
I used Postman to test the API endpoints, and the JSON output is an array. But in my factory, I clearly stated that I didn't want an array with the isArray option.
With the isArray flag you don't specify what you want, you specify what is to be expected on that particular API endpoint.
If you don't want an array, change the backend code, but if it is an array, then use isArray: true in the configuration.
Edit: you can use the tranformResponse property to specify a converter function for the data, which would enable you to use an endpoint returning an object, but still use a resource method configured for an array with isArray:true (and vice versa), see this answer for an example.