Search code examples
javascriptmeteorparse-platformmeteor-blazemeteor-helper

Can't create Meteor.js helper based on parse.com query


My meteor app accesses Parse.com to pull in and display data. I started out integrating the parse.com javascript query directly into the template's rendered function, which worked well.

Now, I want to use the Parse.com query in a helper to pass it over to a meteor {{#each}} loop which lives in my template.

Template.dashboard.helpers({
app: function () {
    //init new array
    var appsArr = [];
    //Create a Parse Query for Post objects
    var query = new Parse.Query("Apps");
    query.descending("createdAt");
    var appsObj = {};
    query.find({
        success: function(results) {
            // Add the returned Parse.Object values to appsArr
            for (var i = 0; i < results.length; i++) {
                appsObj = {};
                appsObj.obid = results[i].id;
                appsObj.title = results[i].attributes.title;
                appsObj.screenshot1 = results[i].attributes.screenshot1._url;
                appsObj.appIcon = results[i].attributes.appIcon._url;
                appsArr.push(appsObj);
            }
        },
        error: function(error) {
            alert("Error: " + error.code + " " + error.message);
        }
    });

    return appsArr
}
});

Every time I try and return my array (appsArr) in the helper I get the error : "Exception in template helper: undefined". I also can't get my parse objects to output in the console. Again, the same code works in the rendered function.

I am fairly new to Meteor.js and Blaze templates. please help me implement this parse query into the helper correctly so I can {{#each}} in the template.

 {{#each app}}
        <h3 class="app-title">{{title}}</h3>
{{/each}} 

Thanks in advance!


Solution

  • Because the query.find function is asynchronous and non-blocking, you can't just assign variables in the callback and return them outside of the callback -- the callback hasn't run by the time you hit the return statement, so you're returning something that hasn't been defined.

    An easy way around this will be to use a reactive variable (a variable whose assignment is watched); you can either use [ReactiveVar][1] or the built-in reactive [Session][2] variable. I typically use Session. A possible implementation would be something like this (apologies for not testing this out ahead of time):

    Template.dashboard.onRendered({ // onRendered, calculate appVar
      Session.set('appsVar', null); // reset appsVar immediately -- can also do this in onCreated / onDestroyed to clean up
      //init new array
      var appsArr = [];
      //Create a Parse Query for Post objects
      var query = new Parse.Query("Apps");
      query.descending("createdAt");
      var appsObj = {};
      query.find({
        success: function(results) {
            // Add the returned Parse.Object values to appsArr
            for (var i = 0; i < results.length; i++) {
                appsObj = {};
                appsObj.obid = results[i].id;
                appsObj.title = results[i].attributes.title;
                appsObj.screenshot1 = results[i].attributes.screenshot1._url;
                appsObj.appIcon = results[i].attributes.appIcon._url;
                appsArr.push(appsObj);
            }
    
            Session.set('appsVar', appsVar);
          },
          error: function(error) {
              alert("Error: " + error.code + " " + error.message);
          }
        });
      }
    });
    
    Template.dashboard.helpers({
      app: function() { return Session.get('appsVar'); } // This will re-run when Session.appsVar is updated in the callback above.
    });