4

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>
Dreverz
  • 43
  • 1
  • 4
  • help me understand why you wired it up the way you did. why have the template level subscribe in the autorun when you're only going to be calling the subscription once (because you're not passing in a reactive variable to the subscription)? Why a autorun within a autorun, for example why not return this.ready() from the publication and then make your call to change color? – Aaron Jun 17 '15 at 18:09

1 Answers1

6

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")
    }
  }
});
Ethaan
  • 11,291
  • 5
  • 35
  • 45
  • Thanks for the reply. I was not able to get it to work unfortunately though. When trying to make use of this. I get the error that my collection variable is not defined. I also hoped to get some help on the second part of my question. Being the observe/autorun when the value I am using changes. – Dreverz Apr 04 '15 at 07:36
  • I was able to get your code working for your first solution on Meteor 1.1. I made a mistake in my code. I just need to figure out how to make the second part of my question work now. – Dreverz Apr 04 '15 at 08:16
  • I have updated my original post with my finished solution. I was able to learn and make use of what you gave an example of to complete my problem. – Dreverz Apr 04 '15 at 09:10