Meteor user helper hangs off after update - javascript

I have this helper:
agreed: function (){
if (Meteor.users.findOne({_id: Meteor.userId(), profile: {agreedTermsOfUse: 'true'}}))
return true;
}
On the page where I check it I have this:
{{#unless agreed}}
agree form
{{else}}
Create item form.
{{list of item}}
{{/unless}}
So far, all goes well. The user signs up then he can create an item and it renders on the list of items..
Now, I've added another Meteor.call, which when getting the success call back on the client, for the creating item, it adds the item id to the users' profile.hasItems.
Then after getting succes for that method, "unless" returns false, and I have to submit the agree to form again.
What am I missing? Thanks.
"submit .create_restaurant": function (event) {
event.preventDefault();
var text = event.target.create_restaurant.value;
Meteor.call('CreateRest', Meteor.userId(), text, function(error, result){
if(error){
}else{
console.log(result, Meteor.userId());
Meteor.call('userRestaurants', result, Meteor.userId(), function (error, result) {
if (error) {
alert(123);
} else {
console.log(result);
}
})
}
}
);
event.target.create_restaurant.value = "";
}
methods:
'CreateRest': function(user_id, title) {
check(title, String);
check(user_id, String);
return callback = Restaurants.insert({
createdBy: user_id,
createdAt: new Date(),
title: title
});
},
'userRestaurants': function(rest_id, createdBy) {
var restId = checkHelper(rest_id, createdBy);
if(restId)
console.log(rest_id, createdBy);
{
var callback = Meteor.users.update(
createdBy,
{$addToSet: {'profile.hasRestaurants': restId}}
);
return callback;
}
}

I don't know why you're seeing the behaviour that you are, but I do know that you have other problems to sort out first :)
You have a huge security hole - you're passing the user id through to the method from the client. That means that anyone can simply open the browser console and create a restaurant with any user id they like as the owner. Instead, use this.userId in the method to get the id of the caller.
Why the round trip to the server? Just have the first method update the client.
So, something like this (untested, written by hand here):
"submit .create_restaurant": function (event) {
event.preventDefault();
var text = event.target.create_restaurant.value;
Meteor.call('CreateRest',text, function(error, result){
if(error){
alert(123);
}else{
console.log(result);
}
});
event.target.create_restaurant.value = "";
}
and:
'CreateRest': function(user_id, title) {
check(title, String);
check(this.userId, String);
userId = this.userId;
Restaurants.insert({
createdBy: userId,
createdAt: new Date(),
title: title
}, function(err, restId) {
if (err) throw new Meteor.Error(err);
Meteor.users.update(
userId,
{$addToSet: {'profile.hasRestaurants': restId}},
function (err, res) {
if (err) throw new Meteor.Error(err);
return restId;
}
);
});
Once that's implemented properly it might start working. If it doesn't then the issue isn't related to the code you're posting.
Finally note that from a schema perspective it's really odd that that you have profile.hasRestaurants. To find the restaurants that a user has you should just do a find on the Restaurants collection.

Related

Update reactive var with server Method

I have a problem with updating some values in Meteor app on client-side. I'm trying to understand how ReactiveVar works.
When I use find() method on collection on client-side the site updates immediately each time I change something. I want to achieve the same effect using ReactiveVar and server-side Method. So the code below works correctly for me:
// Client
Template.body.onCreated(function appBodyOnCreated() {
this.subscribe('activities');
}
Template.body.helpers({
getCounter() {
return Activities.find({
editorId: Meteor.userId(),
'referredObject.type': 'LIST'
}).count();
}
});
But when I try to achieve the same effect with server-side Method it doesn't work correctly. Code below updates variable only once. If I want to get current value I need to refresh the page.
// Server
Meteor.methods({'activitiesCreateCount'(userId, objectType) {
check(userId, String);
check(objectType, String);
return Activities.find({
editorId: userId,
'referredObject.type': objectType
}).count();
}
});
// Client
Template.body.onCreated(function appBodyOnCreated() {
this.subscribe('activities');
this.activitiesAmount = new ReactiveVar(false);
}
Template.body.helpers({
getCounter() {
var tempInstance = Template.instance();
Meteor.call('activitiesCreateCount', Meteor.userId(), 'TODO', function(err, response) {
tempInstance.activitiesAmount.set(response);
});
return Template.instance().activitiesAmount.get();
}
});
How I can improve my code if I want always have a current value of the variable (like in the first client-side only example)?
Try to move Meteor.callto Template.body.onCreated
Something like this
// Server
Meteor.methods({'activitiesCreateCount'(userId, objectType) {
check(userId, String);
check(objectType, String);
return Activities.find({
editorId: userId,
'referredObject.type': objectType
}).count();
}
});
// Client
Template.body.onCreated(function appBodyOnCreated() {
self = this;
this.activitiesAmount = new ReactiveVar(false);
Meteor.call('activitiesCreateCount', Meteor.userId(), 'TODO', function(err, response) {
self.activitiesAmount.set(response);
});
}
Template.body.helpers({
getCounter() {
return Template.instance().activitiesAmount.get();
}
});

How can I get a list of a specified field from a Sails JS Waterline collection?

I have a MySQL table with a url column and I want to get a list of every url value in the table. Structure:
In PHP using Laravel I could have done something like this to get an array of every url column value:
$boards = Board::all()->lists('url');
However, I cannot figure out how to do this using Sails JS. I've noticed that find() without any argument gets every row in the database but I can't find anything in the documentation that would allow me to either get a list of the values for the url column or a way to iterate over the returned collection.
I've tried this:
var boards = Board.find().exec(function(error, _boards) {
if(error)
{
return response.negotiate(error);
}
return _boards;
});
However, I can't actually seem to iterate over the returned data.
Any thoughts?
EDIT:
Full HomepageController.js:
module.exports = {
index: function (request, response) {
var data = {
currentDate: (new Date()).toString(),
boards: Board.query('SELECT url FROM board', function (error, results) {
if (error) {
return response.negotiate(error);
}
return results;
})
};
return response.view('homepage', data);
}
};
EDIT: Working Code:
module.exports = {
index: function (request, response) {
Board.query('SELECT url FROM board', function(error, results) {
if(error)
{
return response.negotiate(error);
}
return response.view('homepage', { currentDate: (new Date()).toString(), boards: results });
});
}
};
You can use a raw .query() to select individual columns:
Board.query('SELECT url FROM board', function (err, results) { ... });
However, keep in mind that your data will still look like this:
[{url: 'row 1 value'}, {url: 'row 2 value'}, ...]

Routing to a specific user profile using iron router (METEOR)

As the question says, I need to create a specific route for every user that I have. In my case employers. Now all the examples on the web are connected with the USERS collection.
In my case I want to route to: "/employer/:_id" but I have the Employer ID in the Collection Employers. So basically I have to get the Employer ID via the key from User ID
Im kinda stuck on returning the Employer ID value to the route...
METHODS.js
getEmployerId: function(currentuser){
employerId = Employer.find({"user":currentuser}).fetch();
return employerId;
}
ROUTER.js
Router.route("/employer/:_id", {
name:"employer",
template:"employer",
layoutTemplate:'employerLayout',
data: function(){
var currentuser = Meteor.userId();
Meteor.call("getEmployerId", currentuser, function(error, result){
if(error){
console.log("error", error);
}
if(result){
return true; // I belive here is where I have to pass it up to the ROUTE
}
});
},
onBeforeAction:function(){
var user = Meteor.userId();
if(!user || !Roles.userIsInRole(user, ['employer'])) {
Router.go("verification");
}else {
this.next();
}
return true;
},
});
And this is how my Employer collection looks like:
meteor:PRIMARY> db.employer.find().pretty()
{
"_id" : "qCFGZa4ogc5LR56PL", // I need this for the route
"createdAt" : ISODate("2015-07-18T13:19:16.098Z"),
"user" : "owfJ4ozrfsp26o8G4" // the key through which i can return the ID, got it from the user session?
}
Anyone has a suggestion how to do this? And is this a good approach for each user(employer) profile? Any tutorial, example or anything that describes an application with user profiles would be much appriciated!
Ok, looks like you're nearly there.
I don't think you want the ::id parameter. You send the user to simply /employer, where he does the sign in, so you have his user id.
Then change getEmployerId to getEmployer: in other words, get the whole of the employer record.
getEmployer: function(currentuser){
return Employer.find({"user":currentuser}).fetch();
}
Then in your data: function of the router instead of returning true you return the record you find. In this way the record is available for your template (that's what the data function is for)
data: function(){
var currentuser = Meteor.userId();
Meteor.call("getEmployer", currentuser, function(error, result){
if(error){
console.log("error", error);
}
if(result){
return result;
}
});
},

angular push adds operator that that results in MongoError

I am implementing the tutorial on the mean stack https://www.youtube.com/watch?v=AEE7DY2AYvI
I am adding a delete feature to remove items from the database on a button click
My client side controller has the following 2 functions to add to db and remove
$scope.createMeetup = function() {
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.$save(function (result) {
$scope.meetups.push(result);
$scope.meetupName = '';
});
}
$scope.deleteMeetup = function() {
item = $scope.meetups[0];
console.log("deleting meetup: " + item["name"]);
Meetup.delete(item);
scope.meetups.shift();
}
My server side has the following code
module.exports.create = function (req, res) {
var meetup = new Meetup(req.body);
meetup.save(function (err, result) {
res.json(result);
});
}
module.exports.remove = function(req, res) {
console.log("GOING TO REMOVE!!!");
console.log(req.query);
item = req.query;
Meetup.remove(item, function (err, results) {
console.log("done");
console.log(err);
console.log(results);
});
}
When I run my code and if I delete an already loaded item in the list, it is removed from Mongodb just fine. But if I add an item to the list and I do not refresh the page, it results in an error at my server that appears as
GOING TO REMOVE!!!
{ '$resolved': 'true',
__v: '0',
_id: '54ec04e70398fab504085178',
name: 'j' }
done
{ [MongoError: unknown top level operator: $resolved]
name: 'MongoError',
code: 2,
err: 'unknown top level operator: $resolved' }
null
I if I refresh the page, the it gets deleted fine. But if I added the entry, angular seems to be adding a new variable $resolved. Why is that happening?
Also another question, What is the proper way to call delete? I call it now but I am not able to put a callback. I want a callback which returns and then I shift the list of items. I tried adding a callback but the code never reaches it.
ie I tried the following
/*
Meetup.delete(item, function () {
console.log("In callback!!");
console.log(returnValue);
console.log(responseHeaders);
$scope.meetups.splice(item);
});
*/
/*Meetup.delete(item,
function (returnValue, responseHeaders) {
console.log("In callback!!");
console.log(returnValue);
console.log(responseHeaders);
$scope.meetups.splice(item);
},
function (httpResponse){
// error handling here
console.log("Need to handle errors");
});
*/
I am very new to node and am confused. Any help is very, very appreciated
Looks like it possible to call item.delete instead of Meetup.delete(item). You can call same methods on model instance. It prevent sending angular properties to server.
But better to make a rest API with delete method
DELETE /meetups/:id
and send just a _id
Meetup.remove({id: item._id});

meteor users not synchronize published sub fields of profile

Working on my social app I've found a strange behavior in the collection Meteor.users, this problem does not occur with other Collections using the same methodologies
I would like to have an initial list of users downloading a minimum number of information for everyone and when I open the panel to a specific user I subscribe a different showing more information if the specified user is a friend of mine.
But after subscribe the client collection Meteor.users is not updated!
CLIENT
Meteor.startup(function() {
Meteor.subscribe('usersByIds', Meteor.user().profile.friends, function() {
//... make users list panel using minimal fields
});
//performed when click on a user
function userLoadInfo(userId) {
Meteor.subscribe('userById', userId, function() {
var userProfile = Meteor.users.findOne(userId).profile;
//...
//make template user panel using full or minimal user fields
//...
//BUT NOT WORK!
//HERE Meteor.users.findOne(userId) keep minial user fields!!
//then if userId is my friend!
});
}
});
SERVER
//return minimal user fields
getUsersByIds = function(usersIds) {
return Meteor.users.find({_id: {$in: usersIds} },
{
fields: {
'profile.username':1,
'profile.avatar_url':1
}
});
};
//return all user fields
getFriendById = function(userId) {
return Meteor.users.find({_id: userId},
{
fields: {
'profile.username':1,
'profile.avatar_url':1
//ADDITIONAL FIELDS
'profile.online':1,
'profile.favorites':1,
'profile.friends':1
}
});
};
//Publish all users, with minimal fields
Meteor.publish('usersByIds', function(userId) {
if(!this.userId) return null;
return getUsersByIds( [userId] );
});
//Publish user, IF IS FRIEND full fields
Meteor.publish('userById', function(userId) {
if(!this.userId) return null;
var userCur = getFriendById(userId),
userProfile = userCur.fetch()[0].profile;
if(userProfile.friends.indexOf(this.userId) != -1) //I'm in his friends list
{
console.log('userdById IS FRIEND');
return userCur; //all fields
}
else
return getUsersByIds( [userId] ); //minimal fields
});
This is a limitation or bug in DDP. See this.
A workaround is to move data out of users.profile.
Like this:
//limited publish
Meteor.publish( 'basicData', function( reqId ){
if ( this.userId ) {
return Meteor.users.find({_id: reqId },{
fields: { 'profile.username':1,'profile.avatar_url':1}
});
}
else {
this.ready();
}
});
//friend Publish
Meteor.publish( 'friendData', function( reqId ){
if ( this.userId ) {
return Meteor.users.find( {_id: reqId, 'friendProfile.friends': this.userId }, {
fields: {
'friendProfile.online':1,
'friendProfile.favorites':1,
'friendProfile.friends':1
}
});
}
else {
this.ready();
}
});
//example user
var someUser = {
_id: "abcd",
profile: {
username: "abcd",
avatar_url: "http://pic.jpg"
},
friendProfile: {
friends: ['bcde', 'cdef' ],
online: true,
favorites: ['stuff', 'otherStuff' ]
}
}
As given in a comment, this link reveals your problem. The current DDP Protocol does not allow publishing of subdocuments. One way to get around this is to create a separate collection with your data but a better way would probably to just remove some of the data and make it a direct object off of your user.
The best way to do this is add the data to your user's profile upon insert and then in the onCreateUser move the data onto the user directly:
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
if (options.profile.publicData) {
user.publicData = options.profile.publicData;
delete options.profile.publicData;
}
user.profile = options.profile;
}
return user;
});
If you are allowing clients to perform user inserts make sure you validate the data better though. This way you can have the online, favorites, and friends in the profile and publish that specifically when you want it. You can then have username and avatar_url in the publicData object directly on the user and just always publish all-the-time.

Categories