Passing external data into a template

Well, a typical Blaze template contains two files. One is a html file, another a javascript file with helper and action functions.

So inside you’d have Template.myTemplate.onCreated with meteor call and Template.myTemplate.helpers with readme helper.

Ok I’m following, I wasn’t really considering the helpers and actions as part of the template - but that of course makes sense.

However I’m still not sure how that code would apply… it’s likely more advanced than I am.

i.e. where is getReadmeFromGithub fitting in? Is that a helper? an action? The setup looks completely alien to me.

Maybe it;d help if I gave more context from my end - I’ve been trying to get a meteor call to actually give me something, and am getting nowhere, it’s always undefined (I’ve tried sync and async methods of using meteor call):

Router.route("/app/settings/billing", {
  name: 'BillingSettings',
  controller: 'DashboardController',
  data: {
    invoices: function() {
      var invoices = Meteor.call("GetStripeInvoices");
      console.log(invoices);
      return invoices;
    }
  },
  action: function () {
    this.render('BillingSettings');
  }
});

I tried sticking the call in data having seen a similar approach in an SO, but I’ve tried it pretty much everywhere, still always undefined and even if it wasn’t I’m not sure this is how I get it into the variable pool.

I had the same end result when trying to get the data into a helper (but this process is even less familiar to me, I’ve at least gotten data into templates via the router and publish/subscribe).

I’ll rewrite it for you in JS with use of normal template helpers like in Meteor tutorial. Will be back in few minutes.

Ah that’s very kind of you, above and beyond but appreciated!

I’d do it like @brajt, call the method at the template level in the onCreated event. The catch is your data is not reactive.

@nathanhornby rewritten as promised. I did it fast without checking, so there’s 1% chance it lacks a bracket somewhere.

template:

Template.myTemplate.onCreated({
  this.readmeVar = new ReactiveVar('');
  Meteor.call('getReadmeFromGithub', 
    FlowRouter.getParam('provider'), 
    FlowRouter.getParam('package'), 
    (function(_this) {
      return function(error, result) {
        if (!error) {
          return Template.instance().readmeVar.set(result);
        }
      };
    })
  (this));
});

Template.myTemplate.helpers({
  readme: function(){
    return Template.instance().readmeVar.get();
  }
});

method:

if (Meteor.isServer) {
  Meteor.methods({
    getReadmeFromGithub: function(provider, packageName) {
      return Meteor.http.call("GET", "https://api.github.com/repos" + provider + "/" + packageName + "/readme", {
        headers: {
          "User-Agent": "brajt",
          "Accept": "application/vnd.github.v3.html"
        }
      });
    }
  });
}

But like I said, you can also put this method.call into separate helper, as long (as you init this helper’s code in your HTML later by {{myHelper}}) or in a “click button” action.

I had to remove the var from the first line as it tripped on that;

Unexpected token this

However even then I’m getting in the (browser) console:

Exception in template helper: TypeError: Cannot read property ‘get’ of undefined

For completeness my version of your code:

Template.BillingSettings.onCreated( function () {
  this.getInvoices = new ReactiveVar(null);
  Meteor.call('GetStripeInvoices', (function(_this) {
    return function(error, result) {
      if (error) {
        console.log(error);
        throw new Meteor.Error( 500, "Couldn't get invoices" );
      }
      return _this.getInvoices.set(result);
    };
  })
  (this));
})
Template.BillingSettings.helpers({
  invoices: function () {
    return this.getInvoices.get();
  }
})

This is feeling awfully complicated considering all I need to do is make some json available to a template :frowning:

(Edit: Thanks a lot for doing that btw @brajt - whether I succeed or not I appreciate it!)

Yeah I could break something in the process of rewriting my code. But it should be enough as an inspiration.

I agree, it gets complex when I look at the JS version. :slight_smile: I did it for a particular use case and for me works fine.

Bugger - again thanks for your time - I’ll continue hashing it out. Not often I wish I could just use PHP :smiley:

That’s a leftover of rewriting Blaze Components to Blaze template. In your case you need to get the reactiveVar from Template.instance().

Template.BillingSettings.helpers({
  invoices: function () {
    return Template.instance().getInvoices.get();
  }
})

I assume you’ve got reactiveVar package in the project. :wink:

Thanks for spotting that, did indeed get rid of the last error! (and yup, I have the ReactiveVar package!)

Running a log within the helper is still giving me undefined, however. Is that expected? i.e.

invoices: function () {
  console.log(Template.instance().getInvoices.get());
  return Template.instance().getInvoices.get();
}

 
undefined

(actually empty first, then undefined…)

I can’t get any output in the template still but I’m not sure if that’s because of how I’m trying to drill into the json - I’m guessing the helper should log it correctly, even if not immediately?

Two things here.

First - is your api get call actually returning any data. Can you check it in your meteor.method with console.log? It doesn’t even have to be in the callback, just do it after the http.call.

Second is the proper way of forcing the meteor.call callback to use the template’s data context. By default, callback creates a new, separate context, which makes it difficult to pass the data to our reactiveVar. In CoffeeScript it’s very easy to implement with the use of => but in JS it’s more tricky. The way I implemented it in the rewritten JS should work, but it wasn’t tested.

So i’d do another console.log right before getInvoices.set(result) and check it.

Perhaps use Template.instance().getInvoices.set(result) there too.

Somebody more fluent with JS would be handy here, as I’m a CS guy. Please, help! :slight_smile:

You can write the JS like this, much easier to read

Template.BillingSettings.onCreated(function () {
  this.getInvoices = new ReactiveVar();

  var self = this;
  Meteor.call('GetStripeInvoices', function (error, result) {
      if (error) {
        console.log(error);
        throw new Meteor.Error(500, "Couldn't get invoices");
      }
      self.getInvoices.set(result);
  });
});
  • Yes, not to worry I’m not leading us up a dark alley, the method is definitely fetching the data correctly from Stripe, and I can log it on the server no problem. It’s test data so I can even share it:

20151102-14:06:39.530(0)? { object: ‘list’,
I20151102-14:06:39.531(0)? data:
I20151102-14:06:39.532(0)? [ { id: ‘in_172fv02Sv5VqXz0f0gdu24nR’,
I20151102-14:06:39.532(0)? object: ‘invoice’,
I20151102-14:06:39.532(0)? amount_due: 1000,
I20151102-14:06:39.532(0)? application_fee: null,
[…]

  • The console log right before getInvoices.set(result) is also undefined. I tried using Template.instance() in place of _this but I received an error:

Exception in delivering result of invoking ‘GetStripeInvoices’: TypeError: Cannot read property ‘getInvoices’ of null

So I’m guessing that isn’t the way to go there.

“Somebody more fluent with JS would be handy here, as I’m a CS guy. Please, help! :smile:

If I wasn’t the one seeking help I might mention that’s your own fault for deviating from the actual language meteor uses :stuck_out_tongue: (Shakes fist at CoffeeScript)

Ah that looks a bit like an SO I came across, definitely a bit simpler! Unfortunately running a log before setting the result still gives me undefined, so it’s functioning the same:

Template.BillingSettings.onCreated(function () {
  this.getInvoices = new ReactiveVar();

  var self = this;
  Meteor.call('GetStripeInvoices', function (error, result) {
      if (error) {
        console.log(error);
        throw new Meteor.Error(500, "Couldn't get invoices");
      }
      console.log(self.getInvoices.set(result));
      self.getInvoices.set(result);
  });
});

undefined

result will always return undefined immediately, but as you are assigning result to a reactive variable, you should be getting the returned value once the method has returned the data. Also you are logging the result of the setter method there and not the actual result.

If the Meteor.method is returning a value, and you are assigning the result to a reactive var, the data will be there!

Ha, I noticed that actually, but same thing for just logging result!

If the Meteor.method is returning a value, and you are assigning the result to a reactive var, the data will be there!

I did wonder if this was the case… but whatever I try doesn’t work. Is there an a way to dump variables in blaze so I can check?

I’m currently testing with:

{{#with invoices}}
  {{ data.[0].amount_due }}
{{/with}}

Which given the json structure I think should be working… (even have the data[0] changed to data.[0] as per the spacebars docs). But I’ve tried a few variations on that with no luck (but also while testing methods to get the data into the template in the first place… so

You can dump it during the helper autorun:

Template.BillingSettings.helpers({
  invoices: function () {
    var data = Template.instance().getInvoices.get();
    console.log(data);
    return data;
  }
});

Ah, already tried that, a big fat

undefined

:sweat_smile: there’s something wrong somewhere, probably can’t help you much more without a repo!