How do I fetch all rows of a model with its relations? - bookshelf.js

I have the following models:
role.js
let bookshelf = require('../config/database');
var Role,
Roles;
Role = bookshelf.Model.extend({
tableName: 'roles',
users: function() {
return this.hasMany('User');
}
});
Roles = bookshelf.Collection.extend({
model: Role
});
module.exports = {
Role: bookshelf.model('Role', Role),
Roles: bookshelf.collection('Roles', Roles)
};
user.js
let bookshelf = require('../config/database');
var User,
Users;
User = bookshelf.Model.extend({
tableName: 'users',
role: function() {
return this.hasOne('Role');
}
});
Users = bookshelf.Collection.extend({
model: User
});
module.exports = {
User: bookshelf.model('User', User),
Users: bookshelf.collection('Users', Users)
};
My database tables are like this:
user: id, name, username, role_id
role: id, description
I need to get all users with their roles (id and description), then I tried doing this:
User.where({})
.fetch({withRelated: ['role']})
.then(function(result) {
res.json({data: result.toJSON()});
})
.catch(err => {
res.json({error: JSON.stringify(err)});
});
But all I get is this:
{
"error": "{}"
}
What is wrong with my code?

I've found the answer:
User.forge()
.fetchAll({withRelated: ['role']})
.then(function(result) {
if (result === null)
res.json({data: []});
else
res.json({data: result.toJSON()});
})
.catch(err => {
res.json({error: err.message});
});

Related

this.userId not displaying user

I'm publishing users and a second collection called joboffers. The joboffers collection stores the userIds for employer and candidate. I would like to get the candidate name to display next to the joboffer, for an admin page, however nothing I do seems to work. All of the joboffers display but not the candidate name. In my console I just get object .
Path: publish.js
// publish all jobs for admin to view
Meteor.publish('allJobs', function () {
return JobOffers.find({});
});
Meteor.publish('allUsersWithJobs', function () {
var offers = JobOffers.find({}, {fields: {candidateUserId: 1}}).fetch();
var ids = _.pluck(offers, 'candidateUserId');
options = { fields: {username: 1, emails: 1, "profile.firstName": 1, "profile.familyName": 1 } };
return Meteor.users.find({ _id: { $in: ids } }, options);
});
Path: alljoboffers.js
Template.alljoboffers.onCreated(function() {
Meteor.subscribe("allJobs");
Meteor.subscribe("allUsersWithJobs");
});
Template.alljoboffers.helpers({
alljoboffers: function() {
return JobOffers.find({});
},
candidateName: function() {
console.log(this);
var user = this.userId;
var candidate = (user && user.profile && user.profile.firstName);
return candidate;
},
});
Path: alljoboffers.html
{{#each alljoboffers}}
{{positionTitle}}
{{#with candidateName}}
{{profile.firstName}}
{{/with}}
{{/each}}
You're mixing user id and user object. It's a good idea to create a global helper to help keep a convention on how you manage them. This will help reduce the confusion. For example:
Template.registerHelper('usernameById', function(userId) {
var user = Meteor.users.findOne({_id: userId});
return user && user.profile.firstName;
});
Then in your template:
{{#each alljoboffers}}
{{positionTitle}}
{{usernameById candidateUserId}}
{{/each}}

Meteor - Publish error

I'm building an application where teachers and students interact. I trying to publish the classes for teacher/student relationship. How do I publish classes which contain the teacherUserId and the studentUserId.
Path: publish.js
Meteor.publish('classes', function(group){
if (Roles.userIsInRole(this.userId, ['is_teacher'], group)) {
check(id, String);
return Classes.find({teacherUserId: this.userId, studentUserId: id});
} else {
// user not authorized. do not publish secrets
this.stop();
return;
}
});
Path: class.js
Template.classes.onCreated(function() {
var self = this;
self.autorun(function(){
self.subscribe('classes');
});
});
Template.classes.helpers({
studentProfileId: () => {
return FlowRouter.getParam('id');
}
});

Meteor User Accounts - OnSignin

I've changed my schema to include sub-schemas. I want to group relevant data together and make it easier to manage. Unfortanly this has broken my registration process. How do I make the autoform insert firstName into the sub-schema. Any thoughts?
Path: at_config.js
AccountsTemplates.addFields([
{
_id: "details.firstName",
type: 'text',
displayName: "First name",
required: true,
minLength: 1,
maxLength: 37
}
]);
Path: Schema.js
AccountsTemplates.configure({
Schema.UserDetails = new SimpleSchema({
firstName: {
type: String,
optional: false
}
});
Schema.UserProfile = new SimpleSchema({
details: {
type: Schema.UserDetails,
optional: true
},
});
Schema.User = new SimpleSchema({
profile: {
type: Schema.UserProfile,
optional: true
}
});
Meteor.users.attachSchema(Schema.User);
Path: startup.js
Accounts.onCreateUser(function (options, user) {
if (options.profile && options.profile.roles) {
//include the user profile
Roles.setRolesOnUserObj(user, options.profile.roles);
}
if (options.profile) {
// include the user profile
user.profile = options.profile;
}
return user;
});

How to created a load feature during account creation with Meteor

I have a Meteor project where there's generally a delay during the sign up process for new users due to slow connection etc. Is there a way to create a loading screen during this procedure?
I do currently have the sacha:spin package which I use with the iron:router package when different pages are retrieving data to yield. Eg. for my home page I display all the users (mainly for debugging purposes) using this code in my routes:
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading'
});
Router.route('/', {
name: 'home',
controller: 'HomeController'
});
HomeController = RouteController.extend({
template: 'home',
waitOn: function() {
return Meteor.subscribe('users');
}
});
Here's my spinner template (nice and simple):
<template name="loading">
{{> spinner}}
</template>
This setup works so nicely, so I was wondering if it would be easy to implement on my create account procedure that I have here:
Template.signup.events({
'submit #signup-form': function(event, template) {
event.preventDefault();
var validated = true,
username = ...,
email = ...,
firstName = ...,
lastName = ...,
password = ...,
// Validation
if (validated) {
Accounts.createUser({
username: username,
email: email,
password: password,
profile: {
firstName: firstName,
lastName: lastName
}
},
function(err) {
if (err) {
// Handle it
} else {
Router.go('home');
}
});
return false;
}
});
Can I use my loader that I used for my home template with the create account procedure?
I would use reactive-var to handle this.
https://atmospherejs.com/meteor/reactive-var
Then, in your template code:
Template.signup.onCreated( function() {
Template.instance().isLoading = new ReactiveVar(false);
});
Template.signup.helpers({
isLoading() {
return Template.instance().isLoading.get();
}
});
Template.signup.events({
'submit #signup-form': function(event, template) {
event.preventDefault();
var validated = true,
username = ...,
email = ...,
firstName = ...,
lastName = ...,
password = ...,
// Validation
if (validated) {
template.isLoading.set(true);
Accounts.createUser({
username: username,
email: email,
password: password,
profile: {
firstName: firstName,
lastName: lastName
}
},
function(err) {
if (err) {
// Handle it
} else {
Router.go('home');
}
template.isLoading.set(false);
});
return false;
}
});
And in the current signup template you'll need to have the structure:
{{#unless isLoading}}
<!-- YOUR CONTENT GOES HERE -->
{{else}}
{{> loading}}
{{/unless}}

Weird undefined error on server

I have the following meteor method
hasNoPendingPayments: function() {
var userId = Meteor.userId();
console.log(userId); <---------------------- correctly logs userId
var user = Users.findOne({_id: userId }, { fields: { services: 0 } });
console.log(user); <-------------------------- logs 'undefined'
return hasNoPendingPayments(user);
},
This private helper I call from the above
hasNoPendingPayments = function(user) {
// console.log('hasNoPendingPayments ');
// console.log(user);
var payments = Payments.find({ userId: user._id, status: {
$in: [Payments.States.PENDING, Payments.States.PROCESSING]}
});
return payments.count() === 0;
};
And I call it from the client here
Template.payments.created = function() {
this.hasNoPendingPayments = new ReactiveVar(false);v
};
Template.payments.rendered = function () {
Session.set('showPaymentRequestForm', false);
var self = this;
Meteor.call('hasNoPendingPayments', function(error, result) {
if (result === true) { self.hasNoPendingPayments.set(true); }
});
...
However, I get an undefined error on the server when I load the template initially (I marked where in code). Although, when I try call the same query on the client with the same userId, i correctly gets the user record
Any idea as to why this is?
Try with this.
Template.payments.rendered = function () {
Session.set('showPaymentRequestForm', false);
var self = this;
if(Meteor.userId()){
Meteor.call('hasNoPendingPayments', function(error, result) {
if (result === true) { self.hasNoPendingPayments.set(true); }
});
}else{
console.log("Seems like user its not logged in at the moment")
}
Maybe when you make the Meteor.call, the data its not ready
Also just to be sure, when you run Users.findOne({_id: userId }, { fields: { services: 0 } }); on console.log what you get?
Maybe the find is wrong or have some typo
update
Router.map(function()
{
this.route('payments',
{
action: function()
{
if (Meteor.userId())
this.render();
} else{
this.render('login') // we send the user to login Template
}
}
}
or waitOn
Router.map(function () {
this.route('payments', {
path: '/payments',
waitOn: function(){
return Meteor.subscribe("userData"); //here we render template until the subscribe its ready
}
});
});
Meteor stores all the user records in Meteor.users collection
so try Meteor.users.findOne({_id: userId }....)
Instead of Users.findOne({_id: userId }, { fields: { services: 0 } });
in your server method

Resources