autoValue not working when you validate against mongodb collection fields - meteor

I am trying to set a value using autoValue based on already stored values
I am using meteor 1.3.4.1
This used to work in meteor 1.1.0.2
here is my code:
id: {
type: String,
label: "ID",
autoValue: function() {
var isFirstTime = this.field("profile.isFirstTime").value;
var isApproved = this.field("profile.changesApproved").value;
var value = this.field("profile.unapproved_id").value;
var userId = this.userId;
var user = Meteor.users.findOne({_id: userId});
if (user && user.profile && user.profile.id)
{
return user.profile.id;
}
}
}
I expect value of user.profile.id to be returned since user.profile.id has a value in the users collection but I get a value that is passed from input field. How do I get simple-schema to notice collection values as it used to on meteor 1.1.0.2

I think the confusing part here is the 'this' keyword. in the context of the autoValue function(), this may be the object itself, unless some other context is bound to it by SimpleSchema.
So would suggest you trace the code with debugger and check what is the value of this. if it is not what you think it is, check where it is defined

Related

SimpleSchema update error

I'm trying to create a favorite button by saving the usersId to the logged in users account. The concept is, if there is a userId (user is a favorite), else user is not a favorite. The problem is I'm getting an error update failed: Error: Favorites must be an array and I'm not sure what this means.
Path: schema.js
Schema.UserProfile = new SimpleSchema({
"favorites.$.favorite": {
type: Object
}
});
Path: studentlist.js
Template.student.events({
'click .favourite':function(event,template) {
console.log('click');
var candidateId = this._id;
Meteor.users.update({_id: Meteor.userId() }, { $set: { "profile.favorites": candidateId }});
}
});
You have basically two errors.
as of the error, you must have defined Favorites property to be an array. and in the code you're trying to update with $set command.
when you're inserting an item into an array in MongoDB, you've to use $push operator.
and the second problem you'll face after fixing this one would be the improper data type insertion. because you've defined favorite to be an object, but trying to insert a mere id.

angularfire $watch() a property, not the entire object? (firebase, angular)

Is there a way to watch when one property in an object changes? I tried
var unwatch = obj.$watch(function() {
console.log("data changed");
});
That fired when any property in the object changes. I tried
var unwatch = obj.myProperty.$watch(function() {
console.log("data changed");
});
That returned an error message: "TypeError: obj.myProperty.$watch is not a function".
I tried
var myTargetValue = $firebaseObject(ref.myProperty);
That returned an error message: "TypeError: Cannot read property 'ref' of undefined".
You'll have to create a $firebaseObject for the property. But, using the Firebase SDK tends to be more useful than $watch().
JSBin Demo
angular.module('app', ['firebase'])
.constant('FirebaseUrl', 'https://34474165.firebaseio-demo.com/')
.service('rootRef', ['FirebaseUrl', Firebase])
.controller('MyCtrl', MyController);
function MyController($scope, $firebaseObject, rootRef) {
var objRef = rootRef.child('obj');
var obj = $firebaseObject(objRef);
var objMessage = $firebaseObject(rootRef.child('obj/message'));
// watch the whole object
var unwatch = obj.$watch(function(a) {
console.log(a);
});
// watch a child property
objMessage.$watch(function(a) {
console.log(a, 'msg');
});
// get the whole object as it changes
objRef.on('value', function(snap) {
console.log(snap.val());
});
// get the child property as it changes
objRef.child('message').on('value', function(snap) {
console.log(snap.val());
});
}
Short answer - Use vanilla Firebase (i.e. don't use Angularfire)
To watch a string, number or boolean property with Angularfire it's best to use vanilla Firebase:
// firebase reference
db = firebase.database().ref().child("myTable");
// watch the property
db.child("someProperty").on("value", function(snapshot) {
$scope.message = snapshot.val();
});
Why not Angularfire, and $value:
Using $firebaseObject and passing in a reference to a primitive (string, number, boolean) returns an object like the following (reference documentation):
{
$id: "myProperty",
$priority: null,
$resolved: true,
$value: "Hi mom!"
}
In this example, the string I'm trying to watch is contained in the $value property of the returned object. I was inclined to assign the $value property to the $scope, however this wouldn't work:
// WONT WORK
$scope.foo = $firebaseObject(db.child("myProperty")).$value;
There's a clash between Angular's object naming conventions and Firebase's object naming conventions that causes some trouble here. According to this "Important Note" in the documentation, Angular's $watch function ignores properties with a $ prefix. In other words, the view is not going to update if you assign the $value property of the returned object to $scope.
Thus, it's best to just use vanilla firebase to solve this problem (see top). Hope people find this helpful.

meteor - get value from collection serverside

I want to send an email from the current_user(client) to userY(a member) without exposing the emailaddress to the client. So all serverside.
I have the _id of userY (from the router param: email.toUser = Router.current().params._id;) and send it as a value to a method.
In the method function I want to do something like
var to = Meteor.users.find({ _id: email.toUser });
Now when I console.log(to) I get a huge _mongo object instead of the user profile (I expected to be able to log: to.profile.email) what's the best way to get the value from the email field?
What you are seeing is the cursor to the collection set.
To retrieve the records, you can use .fetch()
var to = Meteor.users.find({ _id: email.toUser }).fetch();
console.log(to[0].profile.email);
As you are looking up by ID, you are expecting only 1 result, so you could also use findOne() instead of find() this will return the first element directly.
var to = Meteor.users.findOne({ _id: email.toUser });
console.log(to.profile.email);
EDIT: I'd like to add that replacing { _id: email.toUser } by email.toUser should work. When using just the ID, there is no need to pass an object.
You should change find to findOne
Like this.
var to = Meteor.users.findOne({ _id: email.toUser });
find return the whole Mongo.Collection cursor

Meteor - How To Extract Key Name From Collection?

I have the following in my initialize file to get the values loaded in the database on startup:
Meteor.startup(function() {
if(typeof Person.findOne() === 'undefined') {
Person.insert({
name: "",
gender: ["male", "female", "prefer not to say"],
age: 0
});
}
});
And then in the server/abc.js I have:
Meteor.methods({
checkPerson: function (input) {
for (var key in Person) {
if (input === key) {
...
}
}
}
});
This meteor method checkPerson is called in the client side with a string value being passed as its only argument(input).
I want to check this 'input' string value against the name of the key in the Person Collection.
Person has a key called 'gender'. So for instance, if the 'input' holds the string value 'gender' then the if statement should be true but in my case it comes as false and hence the code inside the if statement is never executed.
Any help/guidance with this will be appreciated.
UPDATE
I searched on mongodb documentation and found here: http://docs.mongodb.org/manual/reference/operator/query/exists/ and also using some help from this thread: (using $exists in Mongo with dynamic key names and the native driver for node)
that I could do something like this:
var checkThis = {};
checkThis[input] = { $exists : true };
var p = Person.findOne(checkThis);
So if it finds one then 'p' holds the record or else it will be undefined. But still the above code does not work.
If I were to put directly:
var p = Person.find({gender: {$exists: true} });
then it works.
So I need assistance in getting the code to work with the variable 'input'.
Mongo is a schemaless database - you can insert any document structure you like into a collection and the data store won't complain. Therefore Person won't be able to indicate which fields conform to the pattern.
The most common way people deal with this problem is to use a package which provides a schema layer on top of mongo. With meteor, a popular choice is SimpleSchema, and its related package AutoForm. SimpleSchema allows you to define which fields should be allowed into a collection, and AutoForm gives you a set of helpers to enforce them in your UI.
If, instead, you prefer not to use a package you could do something like the following:
person.js
var REQUIRED_FIELDS = {
name: String,
gender: ['male', 'female', 'prefer not to say'],
age: Number
};
Person = new Meteor.Collection('person');
Person.isValid = function(person) {
try {
check(person, REQUIRED_FIELDS);
return true;
} catch (_error) {
return false;
}
};
Meteor.methods({
'person.insert': function(person) {
check(person, REQUIRED_FIELDS);
return Person.insert(person);
}
});
my-template.js
Template.myTemplate.events({
submit: function() {
var person = {
name: $('#name').val(),
gender: $('#gender').val(),
age: parseInt($('#age').val(), 10)
};
if (Person.isValid(person))
Meteor.call('person.insert', person);
else
alert('invalid person');
}
});
Here we are using meteor's check package to do some basic field validation. By adding an isValid helper to the Person collection, we can validate the schema without the need for a method call. Best of all we can reuse the same check when inserting a new document.

In Meteor, how to set a reactive dependency on a subpart of a template data context?

Consider the following code :
Template.fullDoc.rendered = function() {
// Get triggered whenever the selected document id changes
this.autorun(function() {
var docId = isolateValue(function() {
return Template.currentData().selectedDoc._id;
});
...
});
}
This code doesn't work. Inside isolateValue(), Template.currentData() sometimes triggers an exception: Exception from Tracker recompute function: Error: There is no current view (this corresponds to the fact that Template.instance() returns null).
So how do you set a reactive dependency on a subpart of a template data context?
You could recreate the isolateValue behaviour in a way which doesn't cause Template.instance() to get set to null sometimes.
$ meteor add reactive-var
Template.fullDoc.rendered = function () {
var docIdVar = new ReactiveVar();
this.autorun(function () {
docIdVar.set(Template.currentData().selectedDoc._id);
});
this.autorun(function () {
var docId = docIdVar.get();
// ...
});
}
This makes use of the fact that setting a ReactiveVar to the same value it already has will not trigger an invalidation. (By default this only works for primitives; for objects you'll need to pass a custom equalsFunc when you construct the ReactiveVar. If _id is a string, you're fine. If it's ObjectID you probably aren't.)

Resources