How to retrieve a specific object with Parse.Query - javascript

I am using Parse to create a WebApp and I am trying to get an instance of an object Productwith this code:
getProduct: function() {
var productClass = Parse.Object.extend("Product");
var query = new Parse.Query(productClass);
var result = query.get(productId, {
success: function(object) {
console.log(object.get("productName"));
},
error: function(object, error) {
...
}
});
return result;
}
I get a:
result.get is not a function
Printing the object only, I realized that I do not get a Product, I get this in the console (Safari):
[Log] Object (views.js, line 269)
_rejected: false
_rejectedCallbacks: Array[0]
_resolved: true
_resolvedCallbacks: Array[0]
_result: Arguments[1]
__proto__: Object
I tried many ways, but I am not able to retrieve a Product object and its attributes. What am I doing wrong?
EDIT
I am adding my Products View:
window.app.Products = Parse.View.extend({
template: _.template($('#products-template').html()),
el: $('body'),
content: $('#content'),
...
initialize: function() {
_.bindAll(this, 'render');
this.render();
},
render: function () {
$(this.content).html(this.template);
return this;
},
...
getUser: function() {
return Parse.User.current();
},
getUserProduct: function() {
var promise = new Parse.Promise();
var productClass = Parse.Object.extend("Product");
var query = new Parse.Query(productClass);
query.equalTo("objectId", this.getUser().get("product").id);
query.first().then(function(result) {
if(result){
// If result was defined, the object with this objectID was found
promise.resolve(result);
} else {
console.log("Product was not found");
promise.resolve(null);
}
}, function(error){
console.error("Error searching for Product. Error: " + error);
promise.error(error);
});
return promise;
},
setProduct: function() {
this.getUserProduct().then(function(result) {
if(result){
console.log(result.get("productName"));
var productName = result.get("productName");
} else {
console.log("Could not set Product");
}
}, function(error){
console.log("Error: " + error);
});
}
});
I was trying by having a list of parameters and updating them like:
info: {
user: '...',
product: '...'
}
Then passing it to the template:
$(this.content).html(this.template(this.info));
But I am not able to update product.

As I wrote this, I realised that you really aren't saving all that much code by pulling the product search into it's own method. My hope is that it will at least demonstrate to you how to create and call your own custom async methods. For example you may have a query which is much more complex than the current query, or it may perform multiple queries before finding the desired response, in which case it would make sense to pull it into it's own method.
Get Product Async Method
This method retrieves the Product with the given objectID
var getProduct = function(productId) {
var promise = new Parse.Promise();
var Product = Parse.Object.extend("Product");
var query = new Parse.Query(Product);
query.equalTo("objectId",productId);
query.first().then(function(result){
if(result){
// If result was defined, the object with this objectID was found
promise.resolve(result);
} else {
console.log("Product ID: " + productId + " was not found");
promise.resolve(null);
}
}, function(error){
console.error("Error searching for Product with id: " + productId + " Error: " + error);
promise.error(error);
});
return promise;
}
Calling Method
An example of a simple method which calls the above method.
var myMethod = function(){
var productID = "12345678";
getProduct(productID).then(function(result){
if(result){
console.log(result.get("productName"));
var productName = result.get("productName");
var productPrice = result.get("productPrice");
// Now that you have some relevant information about your product
// you could render it out to an Express template, or use this
// value in a calculation etc.
} else {
console.log("Product with objectId: " + productID + " was not found");
}
}, function(error){
console.log("Error: " + error);
});
}
Notes
As these methods are asynchronous, there is no real data being
returned in the 'return value' (the method returns a promise).
Instead we return the relevant data as a result of the promise (where
you see promise.resolve(XXX))
It doesn't make any sense to have a
mutable global variable in this Node.js style architecture.

Related

Fetching api and passing up

I keep struggling to understand this, and just am not getting it.
I want to fetch data from an API, and display it in the nativescript playground on iOS.
function getEvent() {
var event6 = "stringy event";
var myObj2 = {
name: 'Ed',
favoriteFood: 'pie'
};
var event8;
var event9 = new Object;
console.log("-----httpmodule ----------------------------");
httpModule.getJSON("https://agile-brushlands-36817.herokuapp.com/events/4.json").then(function(result) {
console.log("event api fetched: " + JSON.stringify(result));
event8 = JSON.stringify(result);
console.log("event8:" + event8);
event9 = parse(event8);
console.log("event9: ");
}, function(error) {
console.error(JSON.stringify(error));
});
console.log("---leave getEvent------------------------------");
return event9;
}`
It nicely logs the "event api fetched" and the data I wanted. But nothing I try will get data out of the function.
I can access event6 and myObj2 nicely outside the function. But nothing dealing with the api data. I've tried "then" everywhere, but still am not understanding the mechanism.
(And why doesn't console.log("event9: "); log this simple string?)
Basically it has nothing to do with NativeScript, it's all about Promise in JavaScript. It's asynchronous, so you can't return the value directly. It will be something like,
function getEvent() {
var event6 = "stringy event";
var myObj2 = {
name: 'Ed',
favoriteFood: 'pie'
};
var event8, event9;
console.log("-----httpmodule ----------------------------");
return httpModule.getJSON("https://agile-brushlands-36817.herokuapp.com/events/4.json")
.then(function (result) {
console.log("event api fetched: " + JSON.stringify(result));
event8 = JSON.stringify(result);
console.log("event8:" + event8);
event9 = parse(event8);
console.log("event9: " + event9);
console.log("---leave getEvent------------------------------");
return event9;
})
.catch(function (error) {
console.error(JSON.stringify(error));
});
}
getEvent()
.then(function (event9) {
// event9 may be undefined, if there was an error while fetching
console.log(event9);
});
Learn more about Promise here

MeteorTips Second Application Tutorial - Validations

I'm working on the Methods 2 chapter of MeteorTips' Second tutorial, which is basically just building a todo application with several lists.
I moved my code for adding tasks to a list into a method, and decided to try adding some validations to the tasks so that it cannot be blank and it cannot be less than 3 characters. I have the validation working, but my code is returning a Match failed error when running check(currentList, String). I can see that it is not fetching the list's ID, and stroing undefined in the variable.
My question is, how do I fix it?
Code in Question
HTML Template
<template name="addTodo">
<form class="add-todo">
Create a task
<input type="text" placeholder="Type a task here..." name="todoName" />
</form>
</template>
JavaScript
if (Meteor.isClient){
...
Template.addTodo.events({
'submit form': function (event) {
event.preventDefault();
}
});
...
Template.addTodo.onRendered(function () {
var validator = $('.add-todo').validate({
submitHandler: function (event) {
var todoName = $('[name="todoName"]').val();
var currentList = this._id;
console.log(todoName + " | " + currentList); // Assuming user inputs New Task, this returns New Task | undefined
Meteor.call('createListItem', todoName, currentList, function (error, results) {
if (error) {
validator.showErrors({
todoName: error.reason
});
} else {
$('[name="todoName"]').val('');
}
});
}
});
});
...
}
Meteor.methods({
...
'createListItem': function (todoName, currentList) {
check(todoName, String);
check(currentList, String);
var currentUser = Meteor.userId();
var data = {
name: todoName,
completed: false,
createdAt: new Date(),
createdBy: currentUser,
listId: currentList
}
if(!currentUser){
throw new Meteor.Error("not-logged-in", "You're not logged in.");
}
return Todos.insert(data);
},
});
If you have a Cloud 9 account, you can view the complete code. The server may not always be running the application as I have a free account:
Editor (read-only): https://ide.c9.io/blueknightone/meteor-todos
You need to maintain the reference to the original this, because your context is changing in your submitHandler function. As a result, you could use var self = this; and then access the template's data context via self.data:
Template.addTodo.onRendered(function() {
var self = this;
var validator = $('.add-todo').validate({
submitHandler: function(event) {
var todoName = $('[name="todoName"]').val();
var currentList = self.data._id;
console.log(todoName + " | " + currentList);
Meteor.call('createListItem', todoName, currentList, function(error, results) {
if (error) {
validator.showErrors({
todoName: error.reason
});
} else {
$('[name="todoName"]').val('');
}
});
}
});
});
If you want to use ES6 arrow functions, you could implement the following approach:
Template.addTodo.onRendered(function() {
var validator = $('.add-todo').validate({
submitHandler: (event) => {
var todoName = $('[name="todoName"]').val();
var currentList = this.data._id;
console.log(todoName + " | " + currentList);
Meteor.call('createListItem', todoName, currentList, function(error, results) {
if (error) {
validator.showErrors({
todoName: error.reason
});
} else {
$('[name="todoName"]').val('');
}
});
}
});
});

How to pass a parameter to Collection parse? backbone.js

How could one pass a parameter through the parse/fetch function?
I want to pass the variable VARIABLE_PARAMETER in the lower Initialize-part.
Otherwise I have to write three mostly identical Collections.
Thank you for you help.
app.js
//--------------
// Collections
//--------------
DiagnoseApp.Collections.Param1_itemS = Backbone.Collection.extend({
model: DiagnoseApp.Models.Param1_item,
url: 'TestInterface.xml',
parse: function (data) {
var parsed = [];
$(data).find(/*VARIABLE_PARAMETER*/).find('PARAMETER').each(function (index) {
var v_number = $(this).attr('Number');
var v_Desc_D = $(this).attr('Desc_D');
parsed.push({ data_type: v_data_type, number: v_number, Desc_D: v_Desc_D});
});
return parsed;
},
fetch: function (options) {
options = options || {};
options.dataType = "xml";
return Backbone.Collection.prototype.fetch.call(this, options);
}
});
This is the way I initialize the app:
//--------------
// Initialize
//--------------
var VARIABLE_PARAMETER = "OFFLINE";
var offline_Collection = new DiagnoseApp.Collections.Param1_itemS();
var offline_Collection_View = new DiagnoseApp.Views.Param1_itemS({collection: offline_Collection});
//VARIABLE_PARAMETER has to be passed here in fetch I guess ??
offline_Collection.fetch({
success: function() {
console.log("JSON file load was successful", offline_Collection);
offline_Collection_View.render();
},
error: function(){
console.log('There was some error in loading and processing the JSON file');
}
});
The fetch method accepts an option argument : http://backbonejs.org/#Collection-fetch
The parse method also accepts an option argument: http://backbonejs.org/#Collection-parse
These objects are actually the same. So you may write:
parse: function (data, options) {
var parsed = [];
$(data).find(options.variableParameter).find('PARAMETER').each(function (index) {
var v_number = $(this).attr('Number');
var v_Desc_D = $(this).attr('Desc_D');
parsed.push({ data_type: v_data_type, number: v_number, Desc_D: v_Desc_D});
});
return parsed;
},
Not sure I understand your question, but if you want to "pass a parameter" from fetch to parse, and if that parameter value doesn't change for a given collection, you could just store it in the collection. You could pass the parameter to fetch as an additional property in options:
fetch: function (options) {
options = options || {};
options.dataType = "xml";
this.variableParameter = options.variableParameter;
return Backbone.Collection.prototype.fetch.call(this, options);
},
And then simply retrieve it
parse: function (data) {
// do something useful with this.variableParameter
// ...
}

Access to nested object json in SoundCloud with ember-data

I am working on a soundCloud json for the favorites songs from an user.
You can see it here
I can access to my favorites tracks but i can not access to the user id and username.
Here the code i am using which returns my favorite properties and i have commented the code which is not working to return the user properties.
I get this error in the console "Uncaught TypeError: item.user.forEach is not a function"
What am i doing wrong? is it the right way to access to my user properties?
model: function(params) {
var artist, favoriteListProxy, self;
self = this;
artist = params.artist;
this.controllerFor('application').set('artistName', artist);
favoriteListProxy = Ember.ArrayProxy.create({
content: []
});
return new Ember.RSVP.Promise(function(resolve, reject) {
return SC.get("/users/" + 'mannaio' + "/favorites", {limit: 40}, function(favorites) {
if (favorites.length) {
favorites.forEach(function(item, index, arr){
var favorite;
favorite = self.createFavoritelist(item, favoriteListProxy);
// return item.user.forEach(function(user, index, arr){
// return user = self.createUser(user, favorite);
// });
});
favorites = favoriteListProxy.get('content')
return resolve(favorites);
}
});
});
},
createFavoritelist: function(favorite, arr) {
var record;
record = this.store.createRecord('favorite', {});
record.setProperties({
id: favorite.id,
title: favorite.title,
artwork_url: favorite.artwork_url,
genre: favorite.genre
});
arr.pushObject(record);
return record;
},
// createUser: function(user, favorite) {
// var record;
// record = this.store.createRecord('user', {});
// record.setProperties(user).set('favorite', favorite);
// return record;
// },
It appears to me that item.user is an Object and not an Array. Therefore it doesn't have a forEach method.
So try:
return self.createUser(item.user, favorite);

Meteor JS: How do I assign the results of Meteor.call to a variable?

I am trying to insert a documents into collections which are all related to each other: Posts, Comments, and Categories. Each document in Comments and Categories must have a PostId field.
I have created a method named insertSamplePost, which should return the id of the post after inserting a document into Posts. I have assigned this method call to a variable like so:
var postId = Meteor.call('insertSamplePost', samplePost, function(error, id) {
if (error) {
console.log(error);
} else {
return id;
}
});
However, when I try to use postId later to insert related comments and categories, it appears to be undefined! Does anyone know what is happening?
Here is my full code:
if (Meteor.isClient) {
Template.post.events({
'click .new-sample-post' : function (e) {
var samplePost = {
title: "This is a title",
description: "This is a description"
};
// Insert image stub
var postId = Meteor.call('insertSamplePost', samplePost, function(error, id) {
if (error) {
console.log(error);
} else {
return id;
}
});
// This returned undefined. :-()
console.log(postId);
var sampleComment = {
body: "This is a comment",
postId: postId
};
var sampleCategory = {
tag: "Sample Category",
postId: postId
};
Comments.insert(sampleComment);
Categories.insert(sampleCategory);
}
});
}
// Collections
Posts = new Meteor.Collection('posts');
Comments = new Meteor.Collection('comments');
Categories = new Meteor.Collection('categories');
// Methods
Meteor.methods({
insertSamplePost: function(postAttributes) {
var post = _.extend(postAttributes, {
userId: "John Doe",
submitted: new Date().getTime()
});
return Posts.insert(post);
}
});
When you do:
var myVar = Meteor.call("methodName", methodArg, function(error, result) {
return result;
}
Your myVar variable will actually be whatever Meteor.call() returns, not what your callback function returns. Instead, what you can do is:
var postId;
Meteor.call('insertSamplePost', samplePost, function(error, id) {
if (error) {
console.log(error);
} else {
postId = id;
}
});
However, as Akshat mentions, by the time the callback function actually runs and asynchronously sets the postId, your insert calls on the other collections will already have run.
This code would actually be a little simpler if you avoid the server method altogether - you can modify the document in your collection's allow callback:
Template.post.events({
'click .new-sample-post' : function (e) {
var samplePost = {
title: "This is a title",
description: "This is a description"
};
var postId = Posts.insert(samplePost);
var sampleComment = {
body: "This is a comment",
postId: postId
};
var sampleCategory = {
tag: "Sample Category",
postId: postId
};
Comments.insert(sampleComment);
Categories.insert(sampleCategory);
}
});
Now you can add the userId and submitted fields in your Posts.allow() callback:
Posts.allow({
insert: function(userId, doc) {
doc.userId = userId;
doc.submitted = new Date().getTime();
return true;
}
});
If you wanted, you can still do the two secondary inserts within the callback for your first insert, in order to make the operation more atomic (in other words, to make sure the secondary inserts don't happen if the first insert fails).
You can use Session to store the results since Session is reactive and client side javascript is asynchronous so you cant assign the result to a variable directly using return.
So the reason you get undefined is because the result of Meteor.call is given in the callback. The callback will yield a result much later and by the time it returns a result the rest of your code will already have run. This is why its a good idea to use Session because you can use it in a template helper.
However for inserting a post its better to just insert the comments and category in the callback itself since you're not displaying the result in the html.
Meteor.call('insertSamplePost', samplePost, function(error, postId) {
if (error) {
console.log(error);
} else {
var sampleComment = {
body: "This is a comment",
postId: postId
};
var sampleCategory = {
tag: "Sample Category",
postId: postId
};
Comments.insert(sampleComment);
Categories.insert(sampleCategory);
}
});
This way if the result is an error the comment and category wont be inserted.

Categories