I am new to using meteor, so I am hoping to receive a very basic explanation of how these functions work, and how I am supposed to be using them. Otherwise, if there is a method that would be better suited to what I am hoping to achieve, the knowledge would be appreciated.
The functionality I was hoping to achieve:
I have a Mongo collection that contains a number value within documents that are assigned to specific users.
I will use the value that I get from the document to populate a width: xx% in some css in-line styling on a 'progressbar'. But the other use I have for it is performing some kind of 'reactive' function that runs whenever this value changes which can update the background color of this progress bar dynamically based off of the current value of the progressbar. Think 'red' for low and 'green' for high:
project.html:
<template name="progressBar">
<div id="progress-bar" style="width:{{curValue}}; background-color:*dynamicColor*;"></div>
</template>
project.js:
Progress = new Mongo.Collection("progress");
Template.progressBar.helpers({
curValue: function () {
return Progress.findOne({user: Meteor.userId()}).curValue;
}
});
The above sometimes works. But it doesn't seem to be reliable and isn't working for me right now. I get errors about cannot read property 'curValue' of undefined. From what I have researched online, that means that I am trying to access this document before the collection has loaded. But I really cannot find a direct solution or wrap my head around how I am supposed to be structuring this to avoid that error.
The next problem is observing changes to that value and running a function to change the background color if it does change.
Here are a few of the types of autorun/observe pieces of code I have tried to make work:
Session.set('currentValue', Progress.findOne({user:Meteor.userId()}).curValue);
Tracker.autorun(function(){
var currentValue = Session.get('currentValue');
updateColor(currentValue);
});
var currentValue = Progress.findOne({user:Meteor.userId()}).curValue);
var handle = currentValue.observeChanges({
changed: function (id, currentValue) {
updateColor(currentValue);
}
});
To sum up the question/problem:
I want to use a value from a mongo db document in some in-line css, and also track changes on that value. When the value changes, I want a function to run that will update the background color of a div.
Update
Using @Ethaan 's answer below, I was able to correct my subscription/template usage of my collection data. I did a bit more digging and come to a greater understanding of the publish/subscribe methods and learned how to properly use the callbacks on subscriptions to run my Tracker.autorun function at the appropriate time after my collection had loaded. I was able to expand on the answer given to me below to include a reactive Tracker.autorun that will run a function for me to update my color based on my document value.
The code that I ultimately got working is as follows:
project.js
if (Meteor.isClient) {
Progress = new Mongo.Collection("progress");
Template.content.onCreated(function () {
var self = this;
self.autorun(function () {
self.subscribe("progress", function(){
Tracker.autorun(function(){
var query = Progress.findOne({user: Meteor.userId()}).level;
changeColor(query);
});
});
});
});
Template.content.helpers({
value: function(){
return Progress.findOne({user: Meteor.userId()}).level;
}
});
function changeColor(value){
//run some code
}
}
if (Meteor.isServer) {
Progress = new Mongo.Collection("progress");
Meteor.publish("progress", function () {
return Progress.find({user: this.userId});
});
}
project.html
<head>
<title>Project</title>
</head>
<body>
{{> standard}}
</body>
<template name="standard">
...
{{> content}}
</template>
<template name="content">
{{#if Template.subscriptionsReady}}
{{value}}
{{else}}
0
{{/if}}
</template>
that means that I am trying to access this document before the collection has loaded
Seems like you get the problem, now lets get ride to some possible solutions.
Meteor version 1.1
If you are using the new meteor version 1.1 (you can check running meteor --version
)
use this.
First on the onCreated
function use this.
Template.progressBar.onCreated(function () {
var self = this;
self.autorun(function () {
self.subscribe("Progress");
});
});
See more about subscriptionReady on the DOCS. Now on the HTML use like this.
<template name="progress">
{{#if Template.subscriptionsReady}}
<div id="progress-bar" style="width:{{curValue}}; background-color:*dynamicColor*;"></div>
{{else}}
{{> spinner}} <!-- or whatever you have to put on the loading -->
{{/if}}
</template>
Meteor under 1.0.4
You can have on the router something like a waitOn:function(){}
waitOn:function(){
Meteor.subscribe("Progress");
}
or since helper are asynchronous do something like this (not recommendable).
Template.progressBar.helpers({
curValue: function () {
query = Progress.findOne({user: Meteor.userId()}).curValue;
if(query != undefined){
return query;
}else{
console.log("collection isn't ready")
}
}
});