Need help setting up a reactive variable

I have data being pulled from the Google API which I am then trying to display on charts. Part of the params required for the API call are “start-date” and “end-date”. For this, I would like to use this bootstrap date range picker for users to be able to select these dates and then have it adjust the API call accordingly. To make the API call I am using this package: percolate:google-api.

Here is an example of the API call:

GoogleApi.get('youtube/analytics/v1/reports', { params: { 'end-date': "2015-10-31", metrics: "views", ids: "channel==MINE", 'start-date': "2015-10-01", metrics: "views", dimensions: "day", sort: "-day"}}, function(error, result) {
});

Here is my example for the date range selector:

Template.apps.rendered = function(){

$('#reportrange span').html(moment().subtract(29, 'days').format('YYYY-MM-DD') + ' - ' + moment().format('YYYY-MM-DD'));

$('#reportrange').daterangepicker({
    format: 'YYYY-MM-DD',
    startDate: moment().subtract(29, 'days'),
    endDate: moment(),
    showDropdowns: true,
    showWeekNumbers: true,
    timePicker: false,
    timePickerIncrement: 1,
    timePicker12Hour: true,
    ranges: {
        'Today': [moment(), moment()],
        'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
        'Last 7 Days': [moment().subtract(6, 'days'), moment()],
        'Last 30 Days': [moment().subtract(29, 'days'), moment()],
        'This Month': [moment().startOf('month'), moment().endOf('month')],
        'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
    },
    opens: 'left',
    drops: 'down',
    buttonClasses: ['btn', 'btn-sm'],
    applyClass: 'btn-primary',
    cancelClass: 'btn-default',
    separator: ' to ',
    locale: {
        applyLabel: 'Submit',
        cancelLabel: 'Cancel',
        fromLabel: 'From',
        toLabel: 'To',
        customRangeLabel: 'Custom',
        daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
        monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        firstDay: 1
    }
}, function(start, end, label) {
    console.log(start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD'), label);
    $('#reportrange span').html(start.format('YYYY-MM-DD') + ' - ' + end.format('YYYY-MM-DD'));
});

};

I can see in my console the proper output/format I need if I add the following to my date range selector example above:

console.log(start.format('YYYY-MM-DD'));
console.log(end.format('YYYY-MM-DD'));

So both my date picker and API call seem to be working fine, but now I would like to make them work together. I would like to have it so when the user first visits the page it is automatically set at “Last 30 Days”, but then if they would like to choose a different date they can do so and have my API call update automatically. So I basically need to grab the output of those two console.logs above and have them update in my API call for: 'end-date': "xxxx-xx-xx" 'start-date': "xxxx-xx-xx"

Something like this: 'end-date': endDate 'start-date': startDate

To do this I will be using reactive vars. I have been trying to create a reactive variable for a few days to make this work but nothing I do seems to work out. I’ve read the Meteor docs on this package, as well as tried to find other examples, but just can’t seem to figure this out.

If anyone has worked with reactive vars, or has some understanding on this and is willing to share any code examples based on what I have above, it would be extremely appreciated and help me out a ton as I’m fairly new to Meteor and really trying to figure this out.
Thanks!

I think the piece you’re missing is the autorun that will call the google API when the selection changes. So create the reactive vars for start and end on created:

Template.apps.onCreated(function() {
  var self = this;
  this.startDate = new ReactiveVar(moment().subtract(29, 'days'));
  this.endDate = new ReactiveVar(moment());
  this.autorun(function(){
    // use this.startDate and this.endDate (formatted)
    GoogleApi.get('youtube/analytics/v1/reports', { params: { 'end-date': "2015-10-31", metrics: "views", ids: "channel==MINE", 'start-date': "2015-10-01", metrics: "views", dimensions: "day", sort: "-day"}}, function(error, result) {
});
  });
})

And then update the reactive vars when the date range picker changes. That will trigger the autorun.

If I may give 2 unsolicited cents, check out ViewModel, you’d end up with a cleaner implementation (no reactive vars needed). Something like this:

<span {{b "text: formatDate(startDate) + ' - ' + formatDate(endDate)"}}></span>
<div {{b "ref: 'reportrange' "}}></div>
ViewModel.mixin({
  formatting: {
    formatDate: function(date) {
      return date.format('YYYY-MM-DD');
    }
  }
})

Template.apps.viewmodel({
  mixin: 'formatting',
  startDate: moment().subtract(29, 'days'),
  endDate: moment(),
  onRendered: function() {
    var self = this;
    this.reportrange.daterangepicker({
      format: 'YYYY-MM-DD',
      startDate: this.startDate(),
      endDate: this.endDate(),
      showDropdowns: true,
      showWeekNumbers: true,
      timePicker: false,
      timePickerIncrement: 1,
      timePicker12Hour: true,
      ranges: {
        'Today': [moment(), moment()],
        'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
        'Last 7 Days': [moment().subtract(6, 'days'), moment()],
        'Last 30 Days': [moment().subtract(29, 'days'), moment()],
        'This Month': [moment().startOf('month'), moment().endOf('month')],
        'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
      },
      opens: 'left',
      drops: 'down',
      buttonClasses: ['btn', 'btn-sm'],
      applyClass: 'btn-primary',
      cancelClass: 'btn-default',
      separator: ' to ',
      locale: {
        applyLabel: 'Submit',
        cancelLabel: 'Cancel',
        fromLabel: 'From',
        toLabel: 'To',
        customRangeLabel: 'Custom',
        daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
        monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        firstDay: 1
      }
    }, function(start, end, label) {
      self.startDate(start);
      self.endDate(end);
    });
  },
  autorun: function() {
    GoogleApi.get('youtube/analytics/v1/reports', { 
      params: { 
        'start-date': this.formatDate(this.startDate()), 
        'end-date': this.formatDate(this.endDate()), 
        metrics: "views", 
        ids: "channel==MINE", 
        dimensions: "day", 
        sort: "-day"
      }
    }, function(error, result) {
      // Do something with result
    });
  }
})
1 Like

Thank you very much for the response! It’s extremely appreciated and has definitely helped me!

Unfortunately I’m still just stuck on a couple of things. With your first example (not using ViewModel), I am attempting to use the following:

Template.apps.onCreated(function() {
  var self = this;
  this.startDate = new ReactiveVar(moment().subtract(29, 'days').format('YYYY-MM-DD'));
  this.endDate = new ReactiveVar(moment().format('YYYY-MM-DD'));
  this.autorun(function(){
    // use this.startDate and this.endDate (formatted)
    GoogleApi.get('youtube/analytics/v1/reports', { params: { 'end-date': this.startDate, metrics: "views", ids: "channel==MINE", 'start-date': this.endDate, metrics: "views", dimensions: "day", sort: "-day"}}, function(error, result) {
      console.log(result);
});
  });
});

I have also added console.log(this.startDate) and console.log(this.endDate). Here is what my console looks like when viewing the page:

I’m not too sure what I’m doing wrong here in order to have just the date appear as a string, but currently it is showing “undefined” for the start-date and end-date in my API call.

This is the other part I’m a little lost on, basically having it update when I select a new date using my date selector. As of now it will only show the initialValue of each reactiveVar (like in the screenshot above). I have tried adding the following, but this doesn’t seem to help set them and have it update to the new specified date:

...
}, function(start, end, label) {
    $('#reportrange span').html(start.format('YYYY-MM-DD') + ' - ' + end.format('YYYY-MM-DD'));
    this.startDate.set(start.format('YYYY-MM-DD')) // Set start date
    this.endDate.set(end.format('YYYY-MM-DD')) // Set end date
...

I have also tried to use the ViewModel package and that example, here what appears in my console with that:

I greatly appreciate the help so far and feel like I’m pretty close to having this work. Is there any chance you can see where I’m going wrong with these?

You’re not using reactivevar get/set. See http://docs.meteor.com/#/full/reactivevar

This seems to be where I’m having most of my problems, I can’t seem to figure out how to work this part out. I’ve read the docs and searched for every example I could find, but unfortunately I’m still quite lost with this.

I believe I need to add:

this.startDate.set(start.format('YYYY-MM-DD'));
this.endDate.set(end.format('YYYY-MM-DD'));

this.startDate.get();
this.endDate.get();

But I’m very lost on how/where to implement this… Here is how it is currently looking:

Template.apps.onCreated(function() {
  var self = this;
  this.startDate = new ReactiveVar(moment().subtract(29, 'days').format('YYYY-MM-DD'));
  this.endDate = new ReactiveVar(moment().format('YYYY-MM-DD'));
  this.autorun(function(){
    // use this.startDate and this.endDate (formatted)
    GoogleApi.get('youtube/analytics/v1/reports', { params: { 'end-date': this.startDate, metrics: "views", ids: "channel==MINE", 'start-date': this.endDate, metrics: "views", dimensions: "day", sort: "-day"}}, function(error, result) {
      console.log(result);
});
  });
});

Template.apps.rendered = function(){

    $('#reportrange span').html(moment().subtract(29, 'days').format('YYYY-MM-DD') + ' - ' + moment().format('YYYY-MM-DD'));

    $('#reportrange').daterangepicker({
        format: 'YYYY-MM-DD',
        startDate: moment().subtract(29, 'days'),
        endDate: moment(),
        showDropdowns: true,
        showWeekNumbers: true,
        timePicker: false,
        timePickerIncrement: 1,
        timePicker12Hour: true,
        ranges: {
            'Today': [moment(), moment()],
            'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
            'Last 7 Days': [moment().subtract(6, 'days'), moment()],
            'Last 30 Days': [moment().subtract(29, 'days'), moment()],
            'This Month': [moment().startOf('month'), moment().endOf('month')],
            'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
        },
        opens: 'left',
        drops: 'down',
        buttonClasses: ['btn', 'btn-sm'],
        applyClass: 'btn-primary',
        cancelClass: 'btn-default',
        separator: ' to ',
        locale: {
            applyLabel: 'Submit',
            cancelLabel: 'Cancel',
            fromLabel: 'From',
            toLabel: 'To',
            customRangeLabel: 'Custom',
            daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
            monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            firstDay: 1
        }
    }, function(start, end, label) {
        $('#reportrange span').html(start.format('YYYY-MM-DD') + ' - ' + end.format('YYYY-MM-DD'));
        //console.log(start.format('YYYY-MM-DD')); outputs formatted start date
        //console.log(end.format('YYYY-MM-DD')); outputs formatted end date
        this.startDate.set(start.format('YYYY-MM-DD'));
        this.endDate.set(end.format('YYYY-MM-DD'));

    });

console.log(this.startDate);
console.log(this.endDate);

};

I left out the .get ones above as I’m really unsure of where those would be placed. I’ve tried a ton of different things but nothing seems to be working, any insight or examples at all would be extremely appreciated. Thanks again for the help so far!

For example here:

  this.autorun(function(){
    // use this.startDate and this.endDate (formatted)
    GoogleApi.get('youtube/analytics/v1/reports', { params: { 'end-date': this.startDate, metrics: "views", ids: "channel==MINE", 'start-date': this.endDate, metrics: "views", dimensions: "day", sort: "-day"}}, function(error, result) {
      console.log(result);
});
  });

You’re using this.startDate. You should use that.startDate.get() and of course capture this

  var that = this;
  this.autorun(function(){
...
1 Like

Ah great thank you very much for that!

My API call is now working and using the initialValue for the start-date and end-date, the only issue seems to be that if I try and select a different date with my date picker nothing happens. If I keep my console open and select different dates the console.log(this.startDate); & console.log(this.endDate); simply remain the same.

I have also tried removing these two lines and it works just the same, so I’m guessing there is an issue with how I have my .set setup?:

    this.startDate.set(start.format('YYYY-MM-DD'));
    this.endDate.set(end.format('YYYY-MM-DD'));

It’s most likely a problem with this. Use that like in the autorun.

I gave that a try but unfortunately it’s still doing the same thing for me.

I’d honestly start learning with some easier and shorter examples and wait for 1000000 lines stuff until you get the basics.

You need to read up on variable scopes in javascript.

that, as you have it defined, belongs only to the Template.apps.onCreated(function() { ... }); scope.

this belong to the same scope but only the parts outside the this.autorun(function(){ ... }); scope.

using this or that from the console isn’t going to give you what you’re looking for. that is undefined in the console, while this is the window object – i.e. the object containing all the globally scoped variables.

The console only has the global scope available to it, so the only variables you can access are ones defined without var in front of them. (But don’t go defining globals in your code unless it’s really necessary.)