Check if data came back from firebase using angular2 - javascript

I have the following code to load data from my firebase which works but it is painfully ugly.
loadData() {
// app.component
this.loadJobCodeSubscription = this.databaseService.getData()
.subscribe(result => {
this.data = null;
var data_raw: Array<any>;
if (Object.keys(result)[0] != '$value') {
data_raw = Object.keys(result).map(function (key) {
return [key, result[key]['firstName'], result[key]['lastName']];
})
// remove extra data downloaded from firebase
jobDataRaw.pop();
jobDataRaw.pop();
}
this.jobCodeData = jobDataRaw;
}, error => {
console.log('Error downloading job codes', error)
})
}
// DatabaseService
getData() {
return this.af.database.object('/jobCodes/' + this.currentUser.company)
}
What happens is that if the branch I am querying does not have data, my "result" will display
Andi if I have data coming back, I will get something like
The only difference (that I can think of to check for) between these two data is that one has a $value key and one does not
That is why I am using that weird if statement to check if that key exists.
Is there a way to check for data in a neater way?
Also, is there a way to cast 'result' to the right format as oppose to 'any' which it currently is
Note. I am using Angular 2 with AngularFire2

First, presuming you have control of the backend, you could alter the return to be more easily understandable. Returning an empty payload is a bit confusing if you really mean "nothing".
Second, dangerous to do this line:
Object.keys(result)[0]
As Object.keys(result) may not be the same in the future, even though it may be deterministic.
I think the safest way to do this would be:
if (Object.keys(result).every(k => k != '$value')) {
That being said, I don't think there's an easier way to determine that given the information you've presented. If you continue with this approach, it would be good to make sure you are guarding your statements carefully, as it seems like
bad data could slip through in the future.

Turns out that if I change the return type of the observable to be the original firebase snapshot, the data looks much better. It allows me to call if(snapshot.val()){}
return this.af.database.object('/jobCodes/' + this.currentUser.company, {preserveSnapshot: true})

Related

How to change json value in a nested (mongoose) callback

I'd like to change a json array within a nested function that I created as a callback within a mongoose query.
I am puzzled why it is not possible to change a json array within this scope. Any attempt results in the original value of 'default' which can be shown by the last console.log(answer)
As I am an Embedded C developer with newbie skills in javascript (at most), I'd like to know how to alter any value in a nested scope. And please share some background why my function shows this type of behaviour.
Any help is warmly welcome...
function pwdGen(body) {
var answer = [{
error: errors["E004"],
user: '',
password: 'default',
timeout: 0,
active: false
}];
user.find().where("usertype").equals("superuser").select("username password").exec(
function (err, users) {
if(users.isArray && users.length==0) {
console.log("exists");
answer[0].password="exist_pwd";
} else {
console.log("empty");
answer[0].password="empty_pwd";
}
}
);
answer[0].user="asdave"
answer[0].timeout=1;
console.log(answer);
return answer;
}
Problem
Javascript is async in nature means it does not execute instructions such as network access synchronously like C, C++ or Java.
Solution
Promises are here for rescue.
You need to do something like this:
var users = user.find().where("usertype").equals("superuser").select("username password").exec();
users.then(function(user) {
if(users.isArray && users.length==0) {
console.log("exists");
answer[0].password="exist_pwd";
} else {
console.log("empty");
answer[0].password="empty_pwd";
}
})
EDIT
It's just that the JS is single threaded means one thing at a time but when it sees any network access request like API call, it moves that instruction to somewhere else in the browser where its gets executed and JS continue with the following instructions when all instructions are executed, the moved part which was executed somewhere else, it's result get merged.
Excellent resource to get the idea.

How do I implement a find-or-create pattern in firestore

The firestore api has me a little mixed up in trying to have a repeatable pattern for find-or-create style functions. I'd like the canonical version to look like this:
// returns a promise resolving to a DocumentSnapshot (I think??)
function findOrCreateMyObject(myObject) {
return findMyObject(myObject.identifier).then(documentSnapshot => {
return (documentSnapshot)? documentSnapshot : createMyObject(myObject);
});
};
I'm not sure if DocumentSnapshot is the appropriate return from this, but I figure the caller may want to inspect or update the result, like this:
return findOrCreateMyObject({ identifier:'foo' }).then(documentSnapshot => {
console.log(documentSnapshot.data.someProperty);
return documentSnapshot.ref.update({ someProperty:'bar' });
});
Assuming I am right about that (please tell me if not), it means that both the find and create functions must return a DocumentSnapshot. This is easy enough for the find...
function findMyObject(identifier) {
let query = db.collection('my-object-collection').where('identifier','=='identifier);
return query.get().then(querySnapshot => {
return (querySnapshot.docs.length)? querySnapshot.docs[0] : null;
});
}
...but rather awkward for the create, and the the gist of my problem. I'd want to write create like this....
function createMyObject(myObject) {
// get db from admin
let collectionRef = db.collection('my-object-collection');
return db.collection('my-object-collection').doc().set(myObject);
}
But I cannot because DocumentReference set() resolves to a "non-null promise containing void". Void? I must read back the object I just wrote in order to get a reference to it? In other words, my idealized create needs to be rewritten to be slower and clunkier...
function createMyObject(myObject) {
// get db from admin
let collectionRef = db.collection('my-object-collection');
return db.collection('my-object-collection').doc().set(myObject).then(() => {
// must we query the thing just written?
return findMyObject(myObject.identifier); // frowny face
});
}
This makes my generic create longer (and unnecessarily so when doing just a create). Please tell me:
is DocumentSnapshot the right "currency" for these functions to traffic in?
Am I stuck with a set() and then another query when creating the new object?
Thanks!
EDIT As an example of where I would apply this, say I have customers, uniquely identified by email, and they have a status: 'gold', 'silver' or 'bronze'. My CRM system decides that someone identifying himself as doug#stevenson.com deserves 'silver' status. We don't know at this point wither Mr. Stevenson is a current customer, so we do a find-or-create...
findOrCreateCustomer({ email:'doug#stevenson.com' }).then(customer => {
customer.update({ status:'silver' });
});
I wouldn't just create, because the customer might exist. I wouldn't just update, because the customer might not exist, or might not meet some other criterion for the update.

React native Array.find is not a function

I'm a junior in js development so this might be a silly question, so apologies.
I'm using firebase in my app to store my strings, I call it once when the app is loaded and keep it in memory for faster access.
Then I use strings.find to find a specific string in the array. All work find when running on iOS, but when running on Android I keep getting this weird error
TypeError: Undefined is not a function (evaluting 'this.strings.find...
Here's my data schema
{'strings': [{'name':'something', 'value':'something'} ... ]
And here's my code
getString(name) {
return this.strings.find(x => x.name == name).value
}
And this is where I define the object
init(onUpdated) {
Promise.all([stringsDb.once('value'), multiLang.once('value')])
.then(([stringsSnapshot, multiLangSnapshot]) => {
this.strings = stringsSnapshot.value
this.multiLang = multiLangSnapshot.value
onUpdated(true)
})
.catch((err) => {
console.log(err.stack)
onUpdated(false)
});
}
This probably happens because the this.strings is not an array. you need to debug it and see if its actually an array or not, my guess is it returns an object, if so then you need to use a reducer or perhaps Object.values or something to convert to an array.
p.s you should use the === operator when comparing the strings

Parse Query find method returns object not array

I am working with a mobile App which uses Parse as a backend and I have an issue with the find function. When running the find function in the format of:
var = firstQuery = (new Parse.Query("MyParseObject"))
.find(),
secondQuery = (new Parse.Query("OtherParseObject")).get(id)
// there is only one object that firstQuery can find
Parse.Promise.when(firstQuery, secondQuery)
.then( function (query1res, query2res) {
// query1res should return only one result wrapped in an array,
// instead query1res is an object without a get method
query1res.forEach (function (res) {
// this fails: cannot get .length of undefined
})
// ... do stuff with the returned data
})
Is there something i am missing? I am sure this used to work before, but now it does not.
It is quite difficult to correctly get in an debug this issue thanks to the way Parse works, but their docs outline that this should return an array, but it does not at this point of time.
Thanks for your help.
Based on the Parse docs, it looks like Parse.Promise.when expects an array, although based on this support thread, the results will be passed in as individual arguments to the then handler. Give this a try:
Parse.Promise.when([firstQuery, secondQuery])
.then(function (query1res, query2res) {
// use query1res and query2res
});
Turns out it was down to a function deeper in the code, which needed to return a promise to chain off of. after adding this the code was happy enough. The function that needed to return the promise was called in the forEach and has nothing to do with the initial two queries, which is what was throwing me.

Deps autorun in Meteor JS

Decided to test out Meteor JS today to see if I would be interested in building my next project with it and decided to start out with the Deps library.
To get something up extremely quick to test this feature out, I am using the 500px API to simulate changes. After reading through the docs quickly, I thought I would have a working example of it on my local box.
The function seems to only autorun once which is not how it is suppose to be working based on my initial understanding of this feature in Meteor.
Any advice would be greatly appreciated. Thanks in advance.
if (Meteor.isClient) {
var Api500px = {
dep: new Deps.Dependency,
get: function () {
this.dep.depend();
return Session.get('photos');
},
set: function (res) {
Session.set('photos', res.data.photos);
this.dep.changed();
}
};
Deps.autorun(function () {
Api500px.get();
Meteor.call('fetchPhotos', function (err, res) {
if (!err) Api500px.set(res);
else console.log(err);
});
});
Template.photos.photos = function () {
return Api500px.get();
};
}
if (Meteor.isServer) {
Meteor.methods({
fetchPhotos: function () {
var url = 'https://api.500px.com/v1/photos';
return HTTP.call('GET', url, {
params: {
consumer_key: 'my_consumer_key_here',
feature: 'fresh_today',
image_size: 2,
rpp: 24
}
});
}
});
}
Welcome to Meteor! A couple of things to point out before the actual answer...
Session variables have reactivity built in, so you don't need to use the Deps package to add Deps.Dependency properties when you're using them. This isn't to suggest you shouldn't roll your own reactive objects like this, but if you do so then its get and set functions should return and update a normal javascript property of the object (like value, for example), rather than a Session variable, with the reactivity being provided by the depend and changed methods of the dep property. The alternative would be to just use the Session variables directly and not bother with the Api500px object at all.
It's not clear to me what you're trying to achieve reactively here - apologies if it should be. Are you intending to repeatedly run fetchPhotos in an infinite loop, such that every time a result is returned the function gets called again? If so, it's really not the best way to do things - it would be much better to subscribe to a server publication (using Meteor.subscribe and Meteor.publish), get this publication function to run the API call with whatever the required regularity, and then publish the results to the client. That would dramatically reduce client-server communication with the same net result.
Having said all that, why would it only be running once? The two possible explanations that spring to mind would be that an error is being returned (and thus Api500px.set is never called), or the fact that a Session.set call doesn't actually fire a dependency changed event if the new value is the same as the existing value. However, in the latter case I would still expect your function to run repeatedly as you have your own depend and changed structure surrounding the Session variable, which does not implement that self-limiting logic, so having Api500px.get in the autorun should mean that it reruns when Api500px.set returns even if the Session.set inside it isn't actually doing anything. If it's not the former diagnosis then I'd just log everything in sight and the answer should present itself.

Categories

Resources