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

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!

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.
});

Related

How to assign array of object to dropdown in angularjs?

Hi I am developing web application in angularjs. I have one html form with several drop downs. I am making api calls to fill data to drop down. Instead of making separate call in one call i am getting all data required to bind all drop downs.
Below is my sample data.
{"status":"Success","msg":"Success","code":"200","data":{"Education":[{"ID":1,"Education":"btech"},{"ID":2,"Education":"Bcom"},{"ID":3,"Education":"BA"},{"ID":6,"Education":"PU"}],"MaritalStatus":[{"ID":1,"MaritalStatus":"sinle"},{"ID":2,"MaritalStatus":"married"}]}
I am trying to bind it to drop down as below.
var martialstatus = new Array();
var Education = new Array();
$http.get(baseurl + 'api' + '/Customer/' + 'PersonalInfoMasters').success(function (data) {
$.map(data.data, function (item) {
Education.push(item[0]);
});
console.log(Education);
}).error(function (status) {
});
Abobe piece of code gets me first item from the each object like first item from education,martial status etc. I would like to bind education object to education array. May i know where i am missing any line of code? Any help would be appreciated.
You don't seem to do any kind of mapping from the original result, so the following would suffice:
Education = data.data.Education;
Note:
The item[0] looks strange - are you trying to achieve something with that ?
I would use lowercase for the name of the variable.
Try something like this:
var MaritalStatus = new Array();
var Education = new Array();
$http.get(baseurl + 'api' + '/Customer/' + 'PersonalInfoMasters').success(function (data) {
$.map(data.data.Education, function (item) {
Education.push(item);
});
$.map(data.data.MaritalStatus, function (item) {
MaritalStatus.push(item);
});
}).error(function (error) {
// handle API errors
});
If you do not even need to format the output, you can simply re-assign the original value of your variable.
$http.get(baseurl + 'api' + '/Customer/' + 'PersonalInfoMasters').success(function (data) {
Education = data.data.Education;
MaritalStatus = data.data.MaritalStatus;
}).error(function (error) {
// handle API errors
});
You can do it this way:
var martialstatus = new Array();
var Education = [];
$http.get(baseurl + 'api' + '/Customer/' + 'PersonalInfoMasters').success(function(data) {
$.map(data.data.Education, function(item) {
Education.push(item);
});
}).error(function(status) {
});
You just need to push item instead of item[0] in the .map() method so it takes the whole Education object and push it in your array.
Otherwise you can just use with no need to use the $.map():
Education = data.data.Education
Shouldn't it be more like:
$.map(data.data.Education, function (item) {
Education.push(item[0]);
});
?

Remove duplicate entries in parse-server

Parse-server doesn't support groupBy for queries. So instead of adapting code to work with the duplicate entries i've decided to create a Job to clean the data.
I've created a cloud function using underscore but the results are not good. It's deleting non-duplicate entries too.
I want to remove a entry if another entry exists with the same post_id and user_id
Parse.Cloud.job("removeDuplicateItems", function(request, status) {
var _ = require("underscore");
var hashTable = {};
function hashKeyForTestItem(testItem) {
var fields = ["user_id", "post_id"];
var hashKey = "";
_.each(fields, function (field) {
hashKey += testItem.get(field) + "/" ;
});
return hashKey;
}
var testItemsQuery = new Parse.Query("Post_shares");
testItemsQuery.each(function (testItem) {
var key = hashKeyForTestItem(testItem);
if (key in hashTable) { // this item was seen before, so destroy this
return testItem.destroy();
} else { // it is not in the hashTable, so keep it
hashTable[key] = 1;
}
}).then(function() {
status.success("removal completed successfully.");
}, function(error) {
status.error("Uh oh, something went wrong.");
});
});
Is there a better way of doing this?

I am trying to write a trigger in parse.com but some error

Parse.Cloud.afterSave("StatusTable", function(request) {
var deviceName = request.object.get("deviceName");
var lastSeen = request.object.get("entryTime");
var sectionName = request.object.get("sectionName");
var LastSeen = Parse.Object.extend("LastSeen");
var query = new Parse.Query(LastSeen);
query.equalTo("deviceName", deviceName);
query.first().then(function(result) {
result.put("lastSeen", lastSeen);
result.put("sectionName", sectionName);
result.save();
});
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
);
I have two tables in parse. StatusTable and LastSeen table.I need to write a trigger which updates lastSeen and sectionName columns of LastSeen table when a successful insert has occurred in StatusTable corresponding to the deviceName.This code is not working.
StatusTable
LastSeenTable
From the Object documentation: https://parse.com/docs/js/api/classes/Parse.Object.html
There is no put() method. Try using set() instead:
query.first().then(function(result) {
result.set("lastSeen", lastSeen);
result.set("sectionName", sectionName);
result.save();
});
Also, are you sure you want your query to use first instead of find? Just to ensure that you are always updating the object that you want.

Parse query (callback) in for loop

New to javascript here, so callbacks are still a little iffy in my brain.
What I'm trying to do is: given a "menu" which is an array of objectId's, query for each foodItem that corresponds to that objectId, get its votes, put it in a min-heap (to determine which are the top 5 items), and return those top 5 items.
My heap at the end is empty because I realize that JavaScript is asynchronous and that when I try to get the heap data, the callback might not have necessarily completed.
If it were just one call, I would just nest the callbacks, but since this is a loop I'm not really sure what to do.
function getTopFoods(menu, heap, callback) {
//go through each objectId, get its foodItem and then its votes, then heap it
console.log("got to TopFoods");
for (var i = 0; i < menu.length; i++) {
var foodID = menu[i];
var FoodItem = Parse.Object.extend("FoodItem");
var foodQuery = new Parse.Query(FoodItem);
foodQuery.equalTo("objectId", foodID);
//get corresponding foodItem
foodQuery.find({
success: function(foodResult) {
//got specific food Item
var votes = foodResult.get("votes");
console.log("votes: " + votes);
if (heap.length < 5) {
heap.queue(foodResult);
} else {
if (votes > heap.peek().get("votes")) {
heap.dequeue();
heap.queue(foodResult);
}
}
},
error: function(error) {
console.log("Food error: " + error.code + " " + error.message);
}
});
}
var topFoods = [];
for (var i = 0; i < 5; i++) {
topFoods[i] = heap.dequeue();
}
callback(topFoods);
}
The easiest way is to use promises; at this stage, this involves using a library (coming to JavaScript proper in ES6). If you want a low-tech solution, just count stuff:
var waitingCount = menu.length;
for (....) {
...
success: function(foodResult) {
...
if (!--waitingCount) {
callback(topFive(heap));
}
},
error: function(error) {
--waitingCount;
...
}
...
}
This is just the basic idea. It would be good if you also decremented the counter on failed responses, since this way a single fail will leave you hanging.
EDIT: Err, obviously, the check needs to go to the bottom of success, not to the top as my snippet indicated before, or you'll miss the last element. I also put in the error case.
EDIT2: As eth3lbert notes, parse.com API also supports promises (I don't work with parse.com, so... thanks for the tip). In that case, here's what you do:
var promises = [];
for (....) {
var promise = foodQuery.find({
...
});
promises.push(promise);
});
Parse.Promise.when(promises).then(function()) {
callback(topFive(heap));
}

The collection has not been initialized - Sharepoint Javascript

I'm getting the following error when attempting to get an enumerator for a collection of lists: "Uncaught Error: The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested."
It happens on the line var listEnumerator = lists.getEnumerator(); it seems to me that there is an issue in my attempt to load lists into the client object with context.load(lists);
Here's the portion of my code that's causing the problem. I've marked the place just before the error is thrown.
//____________________________Required function for accessing the host site's info.___________________________________
function getQueryStringParameter(param) {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == param) {
return singleParam[1];
}
}
}
//____________________________Begin checking for list_________________________
function checkForList(listToFind, typeOfListToCreateIfTheListIsMissing)
{
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostcontext = new SP.AppContextSite(context, hostUrl);
var hostweb = hostcontext.get_web();
var lists = hostweb.get_lists();
context.load(lists);
context.executeQueryAsync(checkIfListExistsUsingEnumerator(listToFind, lists, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
}
//Failed to get lists for some reason
function onQueryFailed(sender, args) {
alert('We failed to retrieve lists. \n' + args.get_message() + '\n' + args.get_stackTrace());
}
//____________________________Does list exist?____________________________
function checkIfListExistsUsingEnumerator(listToFind, lists, hostweb, typeOfList)
{
var listExists = false;
//!!!!!!!!!!!!!!! ERROR HERE !!!!!!!!!!!!!!!!
var listEnumerator = lists.getEnumerator();
var title;
while (listEnumerator.moveNext())
{
title = listEnumerator.get_current().get_title();
if (title == listToFind)
{
listExists = true;
}
}
if (!listExists)
{
alert("It appears that a required list does not already exist. \nClick ok, and we'll automatically create one for you.");
//Create a new list
createList(listToFind, hostweb, typeOfList);
}
else if (listExists)
{
//Do nothing.
}
}
//____________________________If it doesn't, create one on the local site____________________________
function createList(nameOfNewList, hostweb, typeOfList) {
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(nameOfNewList);
if (typeOfList === "events")
{
listCreationInfo.set_templateType(SP.ListTemplateType.events);
}
else if (typeOfList === "contacts")
{
listCreationInfo.set_templateType(SP.ListTemplateType.contacts);
}
var lists = hostweb.get_lists();
var newList = lists.add(listCreationInfo);
context.load(newList);
context.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}
function onListCreationSuccess() {
alert('List created successfully!');
}
function onListCreationFail(sender, args) {
alert('Failed to create the list. ' + args.get_message());
}
I've looked at this question sharepoint javascript collection not initialized error which seems to be fairly similar to mine, but I'm having trouble implementing the solution provided there, making me think my error may be have a different cause.
I've also tried querying for the lists inside of the function that is throwing the error, but that doesn't seem to solve anything.
For a little background, these functions are attempting to read all lists from the app's host site, check to see if a specified list exists, and create a list if no matching list exists. If there's a better way of doing that than what I'm attempting, I'd be open to that too.
Any pointers?
Some things I've tried that don't seem to work:
Changing the Asynchronous query
context.executeQueryAsync(checkIfListExists(listToFind, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
to a Synchronous one.
context.executeQuery(checkIfListExists(listToFind, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
I've figured out an alternate, and shorter way to method of achieving the same goal I was trying to achieve before.
Instead of checking to see if a list does not already exist, I just try to create a list, and the Query fails to create a list if one is already there. (That's good because I don't want to overwrite the list if it is already there.)
I'm not totally sure if there are any undesired side effects of what I'm doing here, but in my tests it produced the desired behavior.
//____________________________Required function for accessing the host site's info.___________________________________
function getQueryStringParameter(param) {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == param) {
return singleParam[1];
}
}
}
//____________________________Create a list if one does not already exist_________________________
function createList(listToCreate, typeOfList)
{
// Create an announcement SharePoint list with the name that the user specifies.
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(listToCreate);
if (typeOfList === "events")
{
listCreationInfo.set_templateType(SP.ListTemplateType.events);
}
else if (typeOfList === "contacts")
{
listCreationInfo.set_templateType(SP.ListTemplateType.contacts);
}
var lists = hostweb.get_lists();
var newList = lists.add(listCreationInfo);
currentContext.load(newList);
currentContext.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}
function onListCreationSuccess() {
alert("We've created a list since one doesn't exist yet." );
}
function onListCreationFail(sender, args) {
alert("We didn't create the list. Here's why: " + args.get_message());
}

Categories

Resources