How to call the $firebaseArray in the cloud function of firebase - angularjs

So in my angular JS web app, I have a function that calls on a node in the firebase database called orderedPlayers and returns it as an array as follows:
$firebaseArray(orderedPlayers)
.$loaded(function(loadedPlayers) {
// function in here
});
When attempting to do something similar in the cloud function I am experiencing problems. Is there a way to return the the players node as an array?
I know i can access the database as follows:
admin.database().ref('orderedPlayers');
but the $firebaseArray doesnt work.

These docs can help: https://firebase.google.com/docs/database/admin/retrieve-data
snapshot.val() will return an object that can be referenced as a key-value array. In your case:
admin.database().ref('orderedPlayers').on("value", function(snapshot) {
var loadedPlayers = snapshot.val();
//access your players here
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});

To the best of my knowledge cloudfunctions does not include firebaseArray. You can instead just make the players children into an array.
so:
let yourArray = [];
admin.database().ref('orderedPlayers').once('value').then(snap => {
snap.forEach(childSnap => {
yourArray.push(childSnap));
});
});
or you can use the children:
let yourArray = [];
admin.database().ref('orderedPlayers').on('child_added',snap => {
yourArray.push(snap);
});
*on('child_added') will always be called at least once so it removes the need to loop over the children.
If you want to query the database by a specific value you just add something like orderByChild('order') after the ref and before calling once or on

Related

Exporting an array within an ".then" doesnt work

I'm new to NodeJS and are only familiar with Java. I'm trying to create a file that creates objects based on a database and adds them to an array. This array I want to be able to export so that I can use it throughout the whole program, but when I try to export the array it doesn't work. I've tried googling and understanding but haven't come across anything that was helpful unfortunately.
I hope that someone can help me understand
I've tried calling module.exports after the ".then" call, but it just returns an empty array because its async.
I've also tried calling module.exports = teams inside the .then call but it didn't work neither.
var teams = [];
function assignTeamsToClasses() {
return new Promise((resolve, reject) => {
getAllTeamsInDb((teamList) => {
teamList.forEach((aTeam) => {
let newTeam = new Team(aTeam['teamid'], aTeam['teamname'], aTeam['teamrank']);
teams.push(newTeam);
});
resolve();
});
})
}
assignTeamsToClasses().then(() => {
module.exports = teams;
});
main.js
var teams = require('./initialize.js');
console.log(teams);
I expect it to return all teams that are in the database. I know that array is not empty when called within the ".then" call, but the export part does not.
Simple
the sequence require() + console.log() is synchronous
assignTeamsToClasses() is asynchronous, i.e. it updates teams at some unknown later point in time.
You'll have to design your module API to be asynchronous, e.g. by providing event listener interface or Promise interface that clients can subscribe to, to receive the "database update complete" event.
A proposal:
module.exports = {
completed: new Promise(resolve =>
getAllTeamsInDb(teams => {
const result = [];
teams.each(aTeam =>
result.append(new Team(aTeam.teamid,
aTeam.teamname,
aTeam.teamrank)
)
);
resolve(result);
})
),
};
How to use it:
const dbAPI = require('./initialize.js');
dbAPI
.completed
.then(teams => console.log(teams))
.catch(error => /* handle DB error here? */);
Every caller who uses this API will
either be blocked until the database access has been completed, or
receive result from the already resolved promise and proceed with its then() callback.

Comparing results from two API calls and returning their difference in MEAN app

EDIT: Since I wasn't able to find a correct solution, I changed the
application's structure a bit and posted another question:
Mongoose - find documents not in a list
I have a MEAN app with three models: User, Task, and for keeping track of which task is assigned to which user I have UserTask, which looks like this:
const mongoose = require("mongoose");
const autopopulate = require("mongoose-autopopulate");
const UserTaskSchema = mongoose.Schema({
completed: { type: Boolean, default: false },
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
autopopulate: true
},
taskId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Task",
autopopulate: true
}
});
UserTaskSchema.plugin(autopopulate);
module.exports = mongoose.model("UserTask", UserTaskSchema);
In my frontend app I have AngularJS services and I already have functions for getting all users, all tasks, and tasks which are assigned to a particular user (by getting all UserTasks with given userId. For example:
// user-task.service.js
function getAllUserTasksForUser(userId) {
return $http
.get("http://localhost:3333/userTasks/byUserId/" + userId)
.then(function(response) {
return response.data;
});
}
// task-service.js
function getAllTasks() {
return $http.get("http://localhost:3333/tasks").then(function(response) {
return response.data;
});
}
Then I'm using this data in my controllers like this:
userTaskService
.getAllUserTasksForUser($routeParams.id)
.then(data => (vm.userTasks = data));
...and because of autopopulate plugin I have complete User and Task objects inside the UserTasks that I get. So far, so good.
Now I need to get all Tasks which are not assigned to a particular User. I guess I should first get all Tasks, then all UserTasks for a given userId, and then make some kind of difference, with some "where-not-in" kind of filter.
I'm still a newbie for all the MEAN components, I'm not familiar with all those then()s and promises and stuff... and I'm really not sure how to do this. I tried using multiple then()s but with no success. Can anyone give me a hint?
You can do at server/API side that will more efficient.
In client side, if you want to do then try below
var userid = $routeParams.id;
userTaskService
.getAllTasks()
.then((data) => {
vm.userTasks = data.filter(task => task.userId !== userid)
});

How to loop through an object and push into an array

What I'm working with
angular2
firebase
What I have
A returned object
Each item in the object has a URL property
What I would like to do
Loop through each of the items in the object and get the URL property
Pass that into a firebase storage variable
Push the URL property into an array to make available for my HTML
note: I have this working by hard coding a url, I just need some assistance looping through the object to get the other urls in the object.
Component.ts
'this.imagesToDisplay' will return an object of object (image below)
this.activatedRoute.data
.subscribe((
data: { issueData: any, issueImageData: any }) => {
this.issueToDisplay = data.issueData;
this.imagesToDisplay = data.issueImageData;
this.testing(this.imageToDisplayArray);
});
Comnponent TS - Testing method
So this is hardcoded and pushes the hardcoded url into the array and displays correctly through databinding with the HTML. Cool. However, I would like to loop through the 'imagesToDisplay' object and get both returned urls.
testing(imageToDisplayArray) {
// this works with one url
var storage = firebase.storage().ref().child("image/-Kosd82P-bmh4SFetuf3/-Kosd8HhGmFiUhYMrlvw/30DD9F39-4684-4AA0-9DBF-3B0F0C3450A4.jpg");
storage.getDownloadURL().then(function(url) {
imageToDisplayArray.push(url);
console.log(imageToDisplayArray);
});
}
Any help here would be massively appreciated.
Note: I think this question here is what I'm trying to do, I'm just not sure how to integrate that into my current code. Any help would be awesome.
Stack Overflow question on firebase storage looping
UPDATE
So, I'm incredibly close now. I just have one issue. The code below loops through the returned data and extract the URL properties. I use the url properties to connect to the firebase storage and return the download URLs. Both of these are logged to the console! Awesome. I now have the URLs i needed! The issue I'm having is, it will only let me push these values to a local array. In this instance 'var array'. I need to push to an array that's outside of the 'activatedRoute' 'method'. Anytime I do, it returns as undefined.
this.activatedRoute.data
.subscribe((
data: { issueData: any, issueImageData: any }) => {
this.issueToDisplay = data.issueData;
this.imagesToDisplay = data.issueImageData;
var array = [];
data.issueImageData.forEach(image => {
// Reference to the image URL
var image = image.url;
// Firebase storage
var storage = firebase.storage();
// Path reference
var imagePathReference = storage.ref().child(image);
// Get Download URL
imagePathReference.getDownloadURL().then(function (url) {
console.log(url);
array.push(url);
})
});
});
I would recommend you to use the features rxjs provides you:
this.activatedRoute.data.switchMap(data => {
let parsedData = {
issueToDisplay: data.issueData,
imagesToDisplay: data.issueImageData
}
let imageUrls$ = data.issueImageData.map(image => {
var imagePathReference = storage.ref().child(image);
return Observable.fromPromise(imagePathReference.getDownloadURL())
});
return Observable.forkJoin(imageUrls$).map((...urls) => {
return Object.assign(parsedData, { urls });
})
}).subscribe(data => {
/*
data looks like this:
{
issueToDisplay: any,
imagesToDisplay: any,
urls: string[]
}
*/
});
If I'm correct, you're looking for a solution to map an array-like object of objects to an actual array of objects.
Here is a way to do just that:
var object = {
'0': {
"url": "url1",
"name": "obj1",
"data": 1234
},
'1': {
"url": "url2",
"name": "obj2",
"data": 5678
},
'length': 2
}
var sliced = Array.prototype.slice.call( object, 0 );
console.log(sliced)
Couple remarks:
If you wonder how this works, check this post.
Alternative syntax that you could encounter looks like [].slice.call()
If you want to perform array-like operations, you can probably do that right away with Array.prototype.<METHOD>.call(object, <CALLBACK>
For example:
Array.prototype.map.call(object, function(el) { // return your enhanced element })
So, this works. Not entirely sure how clean it is. But it works.
this.activatedRoute.data
.subscribe((
data: { issueData: any, issueImageData: any }) => {
this.issueToDisplay = data.issueData;
this.imagesToDisplay = data.issueImageData;
var localImageArray = [];
data.issueImageData.forEach(image => {
// Reference to the image URL
var image = image.url;
// Firebase storage
var storage = firebase.storage();
// Path reference
var imagePathReference = storage.ref().child(image);
// Get Download URL
imagePathReference.getDownloadURL().then(function (url) {
localImageArray.push(url);
})
this.getIssueImageArray(localImageArray);
});
});
}
// Get Image Array
getIssueImageArray(array) {
this.imageToDisplayArray = array;
}

Angular firebase fetching single item has race conditions

I'm using angular and firebase together and I have a products array which i'm storing in my rootscope, though it takes time to load the items.
My issues is that when I go to this page for example directly:
http://localhost/product/greyish-sports-shoes
If I go to the home page, the products load after 2 seconds.. and then only if I click on the product link it takes me to it, and it'll work because products have already been loaded.
It goes to the shoeService which contains the products array, but the items are still not loaded, so it cannot find the product by its slug.
That's the code I use in my run method.
var ref = firebase.database().ref().child('products');
$rootScope.shopProds = $firebaseArray(ref);
My shoeService factory:
function shoeFactory($rootScope) {
this.service = {};
this.service.store = new Store($rootScope.shopProds);
this.service.cart = new Cart();
return this.service;
}
It is important to realize that the $firebaseArray service returns an array that is initially empty. The array is populated asynchronously after the data is returned from the server.
Use the promise returned by the $loaded method attached to the array:
function shoeFactory($rootScope) {
this.service = {};
this.service.storePromise = $rootScope.shopProds.$loaded()
.then ( (shopProds) => {
return new Store(shopProds);
});
this.service.cartPromise = this.service.storePromise
.then ( () => {
return new Cart();
}).catch( (error) => {
console.log("ERROR in shoeFactory");
throw error;
});
return this.service;
}
To avoid race conditions, the code needs to use promises to chain operations.

How to use find/findOne outside of a helper function

I am trying to use a findOne statement to access a document from a collection, within a transform function.
this.helpers({
posts: function () {
return Posts.find(this.getReactively('filter'), {sort: {createdAt: -1}, transform: function (doc) {
var restaurant = Restaurants.findOne(doc.rest_id);
doc.restaurant = restaurant.name;
doc.neighborhood = restaurant.neighborhood;
return doc;
}});
}
});
The problem is that the call on findOne returns nothing (undefined). Similarly, using find.fetch() returns an empty Array.
After troubleshooting, I find that running find or findOne anywhere in my code except directly as a helper (i.e. return Posts.find() works fine) returns nothing. I've read that the client may not have finished subscribing to the data at this point, but nothing ever shows up on the client side.
Running the same Restaurants.find or findOne works fine on the Console, returning the expected documents.
Am I trying to do something not supported by angular-meteor? Are the find functions only supported within the context of a helper?
I think the part that you are missing here is that the transform function is being called by Mongo (i.e. in the Mongo call): this is not a callback function.
So, you need to fetch your documents, then findOne for each fetched document.
this.helpers({
posts: function () {
var my_posts = Posts.find(this.getReactively('filter'), {sort: {createdAt: -1}).fetch();
my_posts.forEach(function (doc) {
var restaurant = Restaurants.findOne(doc.rest_id);
doc.restaurant = restaurant.name;
doc.neighborhood = restaurant.neighborhood;
return doc;
}
});
});

Resources