I’m making an app, where users can leave their comments. All reviews are going to Mongo collection. Every week cron-job takes records from collection and sent them by e-mail.
I used the code below and I got not what was expecting. Email text was just: [Object, object].
Can anyone can explain me how should I properly write this line:
var myMessages = FeedbacksList.find({}).toString();
to get my app work corectly?
ALL CODE:
// Methods
Meteor.methods({
sendEmail: function (to, from, subject, text) {
Email.send({
to: to,
from: from,
subject: subject,
html: text
});
}
});
Meteor.methods({
'feedbacks.insert'(emoji, feed, timeSet) {
FeedbacksList.insert({
feedback: emoji,
knowFrom: feed,
createdAt: timeSet
});
}
});
var myMessages = FeedbacksList.find({}).toString();
// Cron Job for weekly email sending
SyncedCron.add({
name: 'Jura Ataskaitos',
schedule: function(parser) {
// parser is a later.parse object
return parser.text('at 9:00 am on Mon');
},
job: function() {
const sendM = Meteor.call('sendEmail', 'karolis.arbaciauskas#gmail.com', 'karolis#pretendentas.lt', 'test', myMessages);
return sendM;
}
});
// Start Cron
SyncedCron.start();
Note that the following returns a cursor
FeedbacksList.find({})
If you want to get all the records returns by this, then you need to fetch them:
var messages = FeedbacksList.find({}).fetch()
Or you can iterate over them:
FeedbacksList.find({}).fetch().foreach(function(message) {
console.log(message);
});
Related
I have this piece of code in client side:
Tracker.autorun(function () {
if (params && params._id) {
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
}
}
});
params will be passed into the url. So, initially we won't have the department data and the findOne method will return null, and then later on, when data arrives, we can find the department object.
But if user enters an invalid id, we need to return them 404. Using tracker autorun, how can I distinguish between 2 cases:
a. Data is not there yet, so findOne returns null
b. There is no such data, even in server's mongodb, so findOne will also returns null.
For case a, tracker autorun will work fine, but for case b, I need to know to return 404
I would suggest you to subscribe to data inside template, like below so you know when subscriptions are ready, then you can check data exists or not
Template.myTemplate.onCreated(function onCreated() {
const self = this;
const id = FlowRouter.getParam('_id');
self.subscribe('department', id);
});
Template.myTemplate.onRendered(function onRendered() {
const self = this;
// this will run after subscribe completes sending records to client
if (self.subscriptionsReady()) {
const id = FlowRouter.getParam('_id');
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
// found data in db
} else {
// 404 - no department found in db
}
}
});
If you are using Iron-Router, you may try this hack.
Router.route('/stores', function() {
this.render('stores', {});
}, {
waitOn: function() {
return [
Meteor.subscribe('stores_db')
];
}
});
The sample code above will wait for the subscription "stores_db" to complete, before rendering anyhing. Then you can use your findOne logic no problems, ensuring that all documents are availble. This suits your situation.
This is what I used to do before I completely understand MeteorJS publications and subscriptions. I do not recommend my solution, it is very bad to user experience. Users will see the page loading forever while the documents are being download. #Sasikanth gave the correct implementation.
I am working on a real time application and i am using firebase with pure html and javascript (not angularJS).
I am having a problem where i saved user's data to firebase with the given code by firebase :
var isNewUser = true;
ref.onAuth(function(authData) {
if (authData && isNewUser) {
authData['status'] = 'active';
authData['role'] = 'member';
ref.child("users").child(authData.uid).set(authData);
}
});
This will add the authData to the /users/ node. As you can see that i also appended some custom fields to the authData, status and role.
Now i am using this code to get the user's data from firebase and display them.
ref4.on("value", function(snapshot) {
var snapshotData = snapshot.val();
console.log('username: '+snapshotData.status);
});
If i use on('value'), the status get printed out on the console but if i do it this way,
ref4.on("child_added", function(snapshot) {
var snapshotData = snapshot.val();
console.log('status: '+snapshotData.status);
});
It is showing undefined for the status. May i know what's wrong and how to fix this problem. Thank you.
Since value is returning the path provided by ref4, and child_added is returning each child of that path, it's unlikely both are going to have a key status.
Consider this data structure:
{
"users": {
"brucelee": {
"status": "awesome"
},
"chucknorris": {
"status": "awesomerest"
}
}
}
If I now query for this according to your incomplete example:
var ref = new Firebase('https://<instance>firebaseio.com/users/brucelee');
ref.on('value', function(snap) {
// requests the brucelee record
console.log(snap.name(), ':', snap.val().status); // "brucelee: awesome"
});
ref.on('child_added', function(snap) {
// iterates children of the brucelee path (i.e. status)
console.log(snap.name(), ':', snap.val().status); // THROWS AN ERROR, because status is a string
});
So to do this on child_added with a data structure like this (and presumably somewhat like yours), it would look as follows:
ref.on('child_added', function(snap) {
// iterates children of the brucelee path (i.e. status)
console.log(snap.name(), ':', snap.val()); // "status: awesome"
});
I'm defining a route that will show an appointment for a patient. I would like the template to show both the patient information and the appointment information.
I have this published:
Meteor.publish('userAppointment', function(appointmentId){
check(appointmentId, String);
var userId = Appointments.findOne(appointmentId).patientId;
return [
Appointments.find({_id: appointmentId}),
Meteor.users.find({_id: userId}, {fields: {profile: true, emails: true}})
];
});
Unfortunately Iron Router doesn't seem to be successfully waiting on the data subscription to complete before it tries to set the data context.
Note where I put debugger:
Router.route('/admin/appointment/:id', {
name: 'AppointmentShow',
waitOn: function(){
return [
Meteor.subscribe("userAppointment", this.params.id)
]
},
data: function(){
var appointmentId = this.params.id;
debugger
var patientId = Appointments.findOne(appointmentId).patientId;
return {
appointment: Appointments.findOne(appointmentId),
patient: Meteor.users.findOne(patientId)
}
}
});
At the time when debugger stops the code, when I do Meteor.users.find().fetch() and Appointments.find().fetch() in the console only the currently logged-in user (me) is available and there are no appointments available.
I expect to see two users (me and the patient) and one appointment available because that's the data that should be available after the waitOn has finished subscribing.
Am I missing something here?
EDIT----- Still doesn't make sense to me ------
When I change my route to this:
Router.route('/admin/appointment/:id', {
name: 'AppointmentShow',
waitOn: function(){
return [
Meteor.subscribe("userAppointment", this.params.id)
]
},
data: function(){
var appointmentId = this.params.id;
return {
appointment: Appointments.findOne(appointmentId),
// patient: Meteor.users.findOne(Appointments.findOne(appointmentId).patientId)
}
}
});
Appointments.findOne(appointmentId) returns an object:
{
_id: "23efref34qr2",
reason: "coughing",
patientId: "785g45g4f"
}
When my data function only returns
appointment: Appointments.findOne(appointmentId)
it works. But if I have it also return
patient: Meteor.users.findOne(Appointments.findOne(appointmentId).patientId)
I get an error message (can't read property 'patientId' of undefined.) Huh? It was just defined on the line above!
To clarify, I think you should be allowing your data function to run (and rerun when collections are populated), but be careful to make sure your function doesn't throw an error when it runs before data is available. This is a general Meteor pattern.
data: function(){
var appointmentId = this.params.id,
appointment = Appointments.findOne(appointmentId);
return { appointment: appointment,
patient: Meteor.users.findOne(appointment ? appointment.patientId : null) }
}
Sorry about the formatting, I'm doing this from an awful phone...
The problem seems to be that it runs the data function() before running the waitOn, and is therefore timing dependent. I have seen the same problem, and also had to check if the data was actually there.
I'm using balanced-payments and their version 1.1 of balanced.js within Meteor.
I'm trying to create a new customer using
balanced.marketplace.customers.create(formData);
Here is my CheckFormSubmitEvents.js file
Template.CheckFormSubmit.events({
'submit form': function (e, tmpl) {
e.preventDefault();
var recurringStatus = $(e.target).find('[name=is_recurring]').is(':checked');
var checkForm = {
name: $(e.target).find('[name=name]').val(),
account_number: $(e.target).find('[name=account_number]').val(),
routing_number: $(e.target).find('[name=routing_number]').val(),
recurring: { is_recurring: recurringStatus },
created_at: new Date
}
checkForm._id = Donations.insert(checkForm);
Meteor.call("balancedCardCreate", checkForm, function(error, result) {
console.log(result);
// Successful tokenization
if(result.status_code === 201 && result.href) {
// Send to your backend
jQuery.post(responseTarget, {
uri: result.href
}, function(r) {
// Check your backend result
if(r.status === 201) {
// Your successful logic here from backend
} else {
// Your failure logic here from backend
}
});
} else {
// Failed to tokenize, your error logic here
}
// Debuging, just displays the tokenization result in a pretty div
$('#response .panel-body pre').html(JSON.stringify(result, false, 4));
$('#response').slideDown(300);
});
}
});
Here is my Methods.js file
var wrappedDelayedFunction = Async.wrap(balanced.marketplace.customers.create);
Meteor.methods({
balancedCardCreate: function (formData) {
console.log(formData);
var response = wrappedDelayedFunction(formData);
console.log(response);
return response;
}
});
I get nothing back when I submit the form, except that on the server console I do see the log of the form data.
I'm sure I'm not calling some of these async functions correctly. The hard part for me here is that the balanced function are async, but I don't know if they fit into the same mold as some of the examples I've seen.
I've tried to follow this example code.
http://meteorhacks.com/improved-async-utilities-in-meteor-npm.html
Is there a specific change that needs to be done in regard to working with balanced here? Does anyone have any tips for working with Async functions or see something specific about my code that I've done wrong?
Thanks
The NPM utilities Async.wrap does the same thing as the undocumented Meteor function Meteor._wrapAsync, in that it takes an asynchronous function with the last argument function(err, result) {} and turns it into a synchronous function which takes the same arguments, but either returns a result or throws an error instead of using the callback. The function yields in a Fiber until the asynchronous callback returns, so that other code in the event loop can run.
One pitfall with this is that you need to make sure that the function you wrap is called with the correct context. So if balanced.marketplace.customers.create is a prototype method that expects this to be set to something, it will not be set properly unless you bind it yourself, using function.bind or any of the other various library polyfills.
For more information, see https://stackoverflow.com/a/21542356/586086.
What I ended up doing was using a future. This works great, I just need to do better at catching errors. Which will be a question for a pro I think ; - )
Credit should go to user3374348 for answering another similar question of mine, which solved both of these.
https://stackoverflow.com/a/23777507/582309
var Future = Npm.require("fibers/future");
function extractFromPromise(promise) {
var fut = new Future();
promise.then(function (result) {
fut["return"](result);
}, function (error) {
fut["throw"](error);
});
return fut.wait();
}
Meteor.methods({
createCustomer: function (data) {
balanced.configure(Meteor.settings.balancedPaymentsAPI);
var customerData = extractFromPromise(balanced.marketplace.customers.create({
'name': data.fname + " " + data.lname,
"address": {
"city": data.city,
"state": data.region,
"line1": data.address_line1,
"line2": data.address_line2,
"postal_code": data.postal_code,
},
'email': data.email_address,
'phone': data.phone_number
}));
var card = extractFromPromise(balanced.marketplace.cards.create({
'number': data.card_number,
'expiration_year': data.expiry_year,
'expiration_month': data.expiry_month,
'cvv': data.cvv
}));
var associate = extractFromPromise(card.associate_to_customer(customerData.href).debit({
"amount": data.total_amount*100,
"appears_on_statement_as": "Trash Mountain" }));
});
As Andrew mentioned, you need to set the context for the method.
Here's the way you can do that with Async.wrap
Async.wrap(balanced.marketplace.customers, "create");
I'm very new to Meteor.js and I'm finding the documentation a bit hard to understand.
I'm starting with a very simple app where Users will simply be allowed to add existing Games to their profile by clicking a button. The Games are stored in another Meteor Collection.
In rails I would just create a has_and_belongs_to_many relationship but that isn't how Meteor works. I thought the best way would be to add an empty array when the user's account is created - then, when they click the "add game" button it would pass the game's title into the users array.
I have this in my /server/users.js file:
Accounts.onCreateUser(function(options, user){
user.games = [];
return user;
});
Meteor.methods({
addGame: function(title) {
Meteor.users.update(Meteor.userId(), { $addToSet: { games: title}});
}
});
And I'm making a call to the addGame method in my /client/views/games/games_list.js file as such:
Template.gamesList.events({
'click .add-to-chest-btn': function(e){
var title = $(e.target).attr('name');
e.preventDefault();
Meteor.call('addGame', title, function(title){ console.log(title)});
}
});
Am I on the right track or is there a better way to do this?
You're on the right track, but do declare an array instead of an object:
Accounts.onCreateUser(function(options, user){
user.games = [];
return user;
});
Push the value directly instead of an object, and use $addToSet to avoid duplicates in case you push the same gameId multiple times:
Meteor.methods({
addGame: function(gameId) {
Meteor.users.update(Meteor.userId(), { $addToSet: { games: gameId }});
}
});