I suspect I'm not doing this the Meteor way. I'm making a shared, interactive calendar.
I have a calendar template:
<template name="calendar">
<h2>Calendar</h2>
<div class="calendar">{{#each days}}
{{> day}}
{{/each}}
</div>
</template>
With a helper that returns a day object:
{
date: thisDate.getDate(),
dateString: dateString,
done: done,
today: isToday
}
I have a day template:
<template name="day">
<div class="day {{stateString}}">{{date}}</div>
</template>
With some helpers (meetingID
is hardcoded for now for development):
Template.day.helpers({
state: function(){
// retreive from DB
var s = Meetings.findOne({"_id":meetingID}).dates[this.dateString];
return s;
}
stateString: function(){
var stateString;
switch(this.state){
case -1: stateString = "unknown"; break;
case 0: stateString = "unavailable"; break;
case 1: stateString = "available"; break;
}
if(this.done) stateString = "done";
return stateString;
}
});
state() gets the state from the db and stateString() picks the right class name for that state.
When you click on it, you cycle through states (1: available, 0: not available, -1: unknown):
Template.day.events({
"click": function(){
if(this.done) return false; // no state changes for past days!
console.log(e);
var newState = this.state + 1;
if(newState > 1) newState = -1;
var q = "dates."+this.dateString+"."+Meteor.userId()+".state";
console.log(q+ " / "+this.state+" / "+newState);
Meetings.update(meetingID, {$set:{q:newState}});
return false;
}
})
I'm having at least two specific problems:
1) How do I call the state() helper from the click event? 2) My db update doesn't seem to work—it's creating a 'q' document instead of using the string stored in q.
I'm sure this is missing some fundamental understanding of the right way to do this—please help!
This Method is callable from the client and server using Meteor.call. Note that you should only use a Method in the case where some code needs to be callable from the client; if you just want to modularize code that is only going to be called from the server, use a regular JavaScript function, not a Method.
Meteor API Docs. What is Meteor? Meteor is a full-stack JavaScript platform for developing modern web and mobile applications. Meteor includes a key set of technologies for building connected-client reactive applications, a build tool, and a curated set of packages from the Node.js and general JavaScript community.
Error handling. In regular JavaScript functions, you indicate errors by throwing an Error object. Throwing errors from Meteor Methods works almost the same way, but a bit of complexity is introduced by the fact that in some cases the error object will be sent over a websocket back to the client.
Methods are Meteor’s remote procedure call (RPC) system, used to save user input events and data that come from the client. If you’re familiar with REST APIs or HTTP, you can think of them like POST requests to your server, but with many nice features optimized for building a modern web application.
Just expanding on the answer by @mark. You probably want to store state
as a reactive variable so that your stateString
helper will update when the state changes. If I understand correctly, you are not actually trying to use the state
helper in your template - it is just needed for the string helper and the event. First add the reactive-var
package:
meteor add reactive-var
I would recommend doing something like this for your template:
Template.day.created = function() {
this.state = new ReactiveVar();
this.autorun(_.bind(function() {
var meetingDates = Meetings.findOne(meetingID).dates[this.data.dateString];
var currentState = meetingDates[Meteor.userId()].state;
this.state.set(currentState);
}, this);
};
Template.day.helpers({
stateString: function() {
if (this.done) {
return 'done';
}
switch(Template.instance().state.get()) {
case -1: return 'unknown';
case 0: return 'unavailable';
case 1: return 'available';
}
}
});
Template.day.events({
'click': function(event, template) {
if (this.done) {
return;
}
var newState = template.state.get() + 1;
if (newState > 1) {
newState = -1;
}
var modifier = {$set: {}}
modifier.$set['dates.'+this.dateString+'.'+Meteor.userId()+'.state'] = newState;
Meetings.update(meetingID, modifier);
}
});
Access template helpers from anywhere:
Template.<name>.__helpers.get('<helper>').call()
Off the top of my head, one way to persist data between helpers and events in the same template is to store it as a property inside the template instance. So for example, you might have something that looks like this:
Template.day.created = function () {
// here `this` refers to template instance
this.state = -1;
};
Template.day.helpers({
state: function () {
var s = Meetings.findOne({"_id":meetingID}).dates[this.dateString];
Template.instance().state = s;
return s;
},
...
});
Template.day.events({
'click': function () {
...
var state = Template.instance().state;
...
}
});
As for the issue with q
, you have to construct the object prior, otherwise q
will be interpreted as the actual field name rather than a variable (notice how there's no distinction between "q" and q inside the query).
var query = {};
query[q] = newState;
Meetings.update(meetingID, { $set: query });
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With