I am trying to pass a parameter to a template helper. The parameter seems to be passed but then I can't use it as I would like to.
I pass the parameter like this:
{{#if unitFieldExists 'profile'}}
{{profile}}
{{/}}
Helper in /client/lib/helpers.js
Template.registerHelper('unitFieldExists', function(unitField) {
var doc = Units.findOne({_id: this._id }, { unitField : {$exists: true} });
// console tests
console.log(unitField); // Outputs profile
console.log(doc); // Outputs document object
// unitfield if of type string
console.log(typeof unitField + " " + unitField); // string
if (doc.unitField) {
return true;
} else {
return false;
}
});
What I am trying to achieve is to return true to the #if if the document contains the field passed. I am probably over complicating things but I am still learning.
You cannot call helpers with arguments in an #if.
I assume that the template instance's data context is a Unit document because of your use of this._id. If so, can just do;
{{#if profile}}
{{profile}}
{{/if}}
The #if checks whether its argument is truthy.
Related
Here is my template onCreated and helper function. I have provided inline comments to add some clarity. I can see that self.post is eventually set because it shows up on my console when I log Template.instance(). However, when I log Template.instance().post, it is always undefined.
Template.default.onCreated(function() {
var self = this;
self.autorun(function() {
var postId = FlowRouter.getParam('post_id');
self.subscribe('onePost', postId);
self.post = Posts.findOne(FlowRouter.getParam('post_id'));
});
});
Template.default.helpers({
poststage: function(stage) {
console.log(Template.instance()); // I can see post object here
console.log(Template.instance().post; //always undefined
if(Template.instance().post) {
// never true
return Template.instance().post.stage == stage;
}
}
});
Template.default.events({
'submit form':function(event, instance) {
Meteor.call('someFunc', instance.post.anotherField);
}
});
Edit: I should add, I'm trying to avoid writing the query twice because I'm also using it in the Template events. (see code).
This feels like a timing issue. You are subscribing to the onePost publication but you aren't waiting for that subscription to be .ready() before assigning self.post The autorun also seems superfluous. You shouldn't need to rerun that block of code in the onCreated block. I suggest:
Template.default.onCreated(function() {
this.postId = FlowRouter.getParam('post_id');
self.subscription = subscribe('onePost', this.postId);
});
Template.default.helpers({
poststage: function(stage) {
if ( this.subscription.ready() ){
return Posts.findOne(this.postId).stage;
}
}
});
The following code works as intended, ie the helper returns true if there is a document and false if there is no document. However, i am getiing a warning on my console.
"Error: Exception in template helper:
TypeError: Cannot read property 'filepickerId' of undefined
at Object.Template.navigation.helpers.ichk..."
The warning is inconsistent and not sure why that i the case. Once again, the code however works without any flow that i can tell.
Template.nav.helpers({
'ichk': function(){
var ct= Info.findOne({_id:Meteor.userId()});
if (ct.profilepic.filepickerId) {
return true;
}else{
return false;
}
You need a guard. Your helper can be rewritten like this:
Template.nav.helpers({
ichk: function () {
var ct = Info.findOne({ _id: Meteor.userId() });
return !!(ct && ct.profilepic && ct.profilepic.filepickerId);
}
}
If it works you should add one more line in order to get rid of the exception.
Template.nav.helpers({
'ichk': function(){
var ct= Info.findOne({_id:Meteor.userId()});
if(ct){
if (ct.profilepic.filepickerId) {
return true;
}
else{
return false;
}}
In this way you first check if the document exists.
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.
I'm trying to figure out how to prevent a template from updating until Meteor.users.update() finishes.
First I'm trying to make sense of the documentation and the use of an optional callback argument in order to sort out what is happening.
Here is what I have:
Meteor.users.update(Meteor.userId(),
{$set:{'profile.reviewList': []}},
[],
function(err, result){
if (err){
console.log('oh no!');
} else {
console.log('Result achieved: '+this.profile.reviewList);
}
});
Currently the console.log('Result achieved: '+this.profile.reviewList); always returns something like ...TypeError: Cannot read property 'reviewList' of undefined... the first time though which tells me its firing before the result comes back.
I'm sure I'm not implementing the callback properly, but I was attempting to model this answer: How do you ensure an update has finished in meteor before running a find?
I'd really just like to delay the re-rendering of the associated template until the property gets created.
Any help will be greatly appreciated.
You assume that scope (this) in callback function return user object, which is wrong.
If you want to get user object in that callback simply query it there:
var user = Meteor.users.find(Meteor.userId()).fetch()
Another thing, you passed empty array as 2nd argument which is not needed.
Meteor.users.update(
Meteor.userId(),
{
$set: {
'profile.reviewList': 'testData'
}
},
function(err, result) {
if (err) {
console.log('oh no!');
} else {
var user = Meteor.users.find(Meteor.userId()).fetch();
console.log('Result achieved: ' , user && user.profile && user.profile.reviewList);
}
}
);
I am trying to generate a Flickr url based on a Flickr API call, and then return that result to a handlebars.js template. I am struggling to find a way around asynchronous processes.
I have tried to create a callback function, but I am still uncertain how to get a defined object or variable into the HTML template.
Here is the code for the Flickr API function:
var FlickrRandomPhotoFromSet = function(setID,callback){
Meteor.http.call("GET","http://api.flickr.com/services/rest/?method=flickr.photosets.getPhotos&api_key="+apiKey+"&photoset_id="+setID+"&format=json&nojsoncallback=1",function (error, result) {
if (result.statusCode === 200)
var photoResult = JSON.parse(result.content);
var photoCount = photoResult.photoset.total;
var randomPhoto = Math.floor((Math.random()*photoCount)+1);
var selectedPhoto = photoResult.photoset.photo[randomPhoto];
var imageURL = "<img src=http://farm"+selectedPhoto.farm+".staticflickr.com/"+selectedPhoto.server+"/"+selectedPhoto.id+"_"+selectedPhoto.secret+"_b.jpg/>";
FlickrObject.random = imageURL;
}
if (callback && typeof(callback)==="function") {
callback();
}
});};
My template code is this:
Template.backgroundImage.background = function(){
FlickrRandomPhotoFromSet(setID,function(){
return FlickrObject;
});
};
But this still leaves me stuck, not able to get a defined object into my HTML, which is coded as such:
<template name="backgroundImage">
<div id="background">
{{random}}
</div>
Use Session as an intermediary. It is reactive so as soon as its set it will change the template with the new data:
Template.backgroundImage.background = function(){
return Session.get("FlickrObject");
};
Template.backgroundImage.created = function() {
FlickrRandomPhotoFromSet(setID,function(){
Session.set("FlickrObject", FlickrObject)
});
}
So the created method will be run when the template is created to run FlickrRandomPhotoFromSet, when the result is returned it will set the Session hash which in turn will set the background as soon as the result is received.
Be careful with your FlickrRandomPhotoFromSet too, I didn't notice you had an argument for FlickrObject to pass to the callback.