Getting the reference from $firebaseArray to make a new reference object - firebase

Related to this question, I need to be able to get an element from a collection as a Firebase reference, ie given a $firebaseArray I need a $firebaseObject pointing to one of its elements. Unlike that example, I can't just hard-code a path the array and take a child from there because the location of the array will vary. And I can't use $firebaseArray.$getRecord() or the object provided by my ng-repeat followed by array.$save() because I may need to do a push() on this element.
So I settled on this reusable approach:
In a service:
function selectElement(array, element) {
var obj = $firebaseObject(array.$ref().child(element.$id));
return obj;
}
In the controller:
function onItemClicked(e) {
vm.selected = dataservice.selectElement(vm.observations, e);
}
In the template:
<div class="list-item" ng-repeat="o in vm.observations" ng-click="vm.onItemClicked(o)">
The first line of selectElement produces an error: array.$ref(...).child is not a function at Object.selectElement
Here array has all the properties you'd expect, but logging array.$ref() shows this obfuscated object:
Y {k: Ji, path: P, n: Ce, pc: true}
That is what it looks like from the time the array is created. What's going on here and how do I use this reference? Is there another way to get a working Firebase object out of an array?

Related

Firebase get child_added in loop

I'm using Ionic 2.
This is my Firebase structure
I want to get the child of java and math in this firebase photo their child are 0,1 to each one.
I did something like that
public getCourse(departmentId:any):any{
var semesterRef = firebase.database().ref('Courses/'+departmentId+'/SemA')
semesterRef.on('child_added', function(courseSnapshot) {
console.log(courseSnapshot.key);
});
but it gives me just the Java and math names and not their child. How can I run in loop and get it and also return as array.
that's my code
You can use the snapshot's forEach method to iterate the key's children:
public getCourse(departmentId:any):any{
var semesterRef = firebase.database().ref('Courses/'+departmentId+'/SemA')
semesterRef.on('child_added', function(courseSnapshot) {
console.log(courseSnapshot.key);
courseSnapshot.forEach(function(childSnapshot):boolean{
console.log(childSnapshot.key);
// ... etc.
return false; // to appease TypeScript
});
});
}
The Firebase API will not return arrays, as it converts arrays to objects with keys derived from the array indices, but you can enumerate the children and re-build an array if that's what you require.

AngularFire / Firebase - How Do You Delete Key That is More Than One Level Deep?

I have an synced object that is initialized like this:
var sync = $firebase(ref);
var syncObject = sync.$asObject();
syncObject.$bindTo($scope, 'data');
The synced object has the following structure:
{
levelone: {
leveltwo: {
valueone: 1
valuetwo: 2
}
}
}
I would like to remove one of the values with $remove(), but I am not able to delete anything other than the entire object with this method.
The following will delete the entire object:
syncObject.$remove('levelone.leveltwo.valueone');
I do not want to use $save() to overwrite the entire synced object.
Is there anyway to remove a nested key with $remove()?
There is no way to drill through multiple layers with the $remove() function. I ended up using vanilla Firebase JS library methods to do the delete.
var ref = new Firebase(myFirebaseURL);
// The child() method (from the vanilla JS library) allows you to target keys
// more than one level deep
ref.child('levelone/leveltwo/valueone').remove();
var sync = $firebase(ref);
You can chain the child() function off of the original JS Firebase class object and then run the remove() method off of that (notice that this is the remove() method and not the $remove() method).

Why Does Firebase orderByChild() return undefined?

I've got a Firebase with a simple bit of data:
There's a list of "players", each with a self-generated GUID, and each containing a value "Count". At my request (e.g. using once()), I want to be able to query the players sorted by the Count value. So, based on the Firebase documentation, I'm using orderByChild(), but it always comes up as undefined when I run the code:
var fb = new Firebase("https://morewhitepixels.firebaseio.com/");
fb.child("players").orderByChild("Count").once("value",function(data) {
// do something with data
});
But this code always returns Uncaught TypeError: undefined is not a function pointing to that second line of code.
What am I missing?
I'm not sure what you do inside the callback, but this works fine:
fb.child("players").orderByChild("Count").once("value",function(data) {
console.log(data.val());
});
Keep in mind that the data parameter is not the actual data yet. It's a DataSnapshot on which you have to call val() first.
You'll probably want to loop through the children, which you can do like this:
fb.child("players").orderByChild("Count").once("value",function(data) {
data.forEach(function(snapshot) {
console.log(snapshot.val().Count);
});
});
The above example prints out all your children in the order you requested:
120320
181425
185227
202488
202488
202488
202488
245197
245197
487320
Alternatively you can use on('child_added' instead:
fb.child("players").orderByChild("Count").on("child_added",function(snapshot) {
console.log(snapshot.val().Count);
});

what is #params in Iron:router

with meteor's IronRouter, I'm trying to use the this.params object elsewhere, but confused as to what it is. It seems to be a zero length array, that is actually an object with named methods after the path components.
# coffee
#route 'magnets',
path: '/magnets/lesson/:lessonCname'
data: ->
if #ready()
debugger;
console.log("route.params", #params)
with this code, in the debug console I will get:
this.params
[]
this.params.lessonCname
"despite-magnets-01"
typeof(this.params)
"object"
this.params.length
0
this.ready()
but in passing the params object to a server method, the methods (ie "lessonCname") disappear.
If my understanding is correct, then the near-term question is what is the best way to retrieve/convert these methods to {property:value} so they can be serialized and passed to server calls?
There are two easy ways of solving your problem, you can either set a global variable from within the data scope (but this is considered bad practice, at least IMO) or you can use the "data" function, which returns the data context for the current template:
data: ->
window._globalscopedata = #params.whatever #setting global variable
return someCollection.findOne #returns data context
_id: #params.whatever
when proccessing this route I will have the whatever param available in _globalscoredata and my document available in the template context.
Take a look at the source code for retrieving the parameters from a path. params is an array, but may have named properties. To iterate over everything, you can use the for in loop:
for(var x in myArray){
// Do something.
}
In this way, you can copy over everything to a new object (there may be a simpler way to create a copy).
The params property attached to a RouteController is an object with the following properties :
hash : the value of the URL hash.
query : an object consisting of key/value pairs representing the query string.
a list of URL fragments with their name and actual value.
Let's take an example, for this route definition :
// using iron:router#1.0.0-pre2 new route definition
Router.route("/posts/:slug");
And this URL typed in the browser address bar : /posts/first-post#comments?lang=en
We can use the console to find out precisely what params will actually contain :
> Router.current().params
Which will display this result :
Object {
hash: "comments",
slug: "first-post",
query: {
lang: "en"
}
}
Here slug is already a property of the params object whose value is "first-post", this is not a method.
If you want to extract from params these URL fragments as an object of key/value pairs, you can use underscore omit :
// getting rid of the hash and the query string
var parameters=_.omit(this.params,["hash","query"]);

How to work with async code in Mongoose virtual properties?

I'm trying to work with associating documents in different collections (not embedded documents) and while there is an issue for that in Mongooose, I'm trying to work around it now by lazy loading the associated document with a virtual property as documented on the Mongoose website.
The problem is that the getter for a virtual takes a function as an argument and uses the return value for the virtual property. This is great when the virtual doesn't require any async calls to calculate it's value, but doesn't work when I need to make an async call to load the other document. Here's the sample code I'm working with:
TransactionSchema.virtual('notebook')
.get( function() { // <-- the return value of this function is used as the property value
Notebook.findById(this.notebookId, function(err, notebook) {
return notebook; // I can't use this value, since the outer function returns before we get to this code
})
// undefined is returned here as the properties value
});
This doesn't work since the function returns before the async call is finished. Is there a way I could use a flow control library to make this work, or could I modify the first function so that I pass the findById call to the getter instead of an anonymous function?
You can define a virtual method, for which you can define a callback.
Using your example:
TransactionSchema.method('getNotebook', function(cb) {
Notebook.findById(this.notebookId, function(err, notebook) {
cb(notebook);
})
});
And while the sole commenter appears to be one of those pedantic types, you also should not be afraid of embedding documents. Its one of mongos strong points from what I understand.
One uses the above code like so:
instance.getNotebook(function(nootebook){
// hey man, I have my notebook and stuff
});
While this addresses the broader problem rather than the specific question, I still thought it was worth submitting:
You can easily load an associated document from another collection (having a nearly identical result as defining a virtual) by using Mongoose's query populate function. Using the above example, this requires specifying the ref of the ObjectID in the Transaction schema (to point to the Notebook collection), then calling populate(NotebookId) while constructing the query. The linked Mongoose documentation addresses this pretty thoroughly.
I'm not familiar with Mongoose's history, but I'm guessing populate did not exist when these earlier answers were submitted.
Josh's approach works great for single document look-ups, but my situation was a little more complex. I needed to do a look-up on a nested property for an entire array of objects. For example, my model looked more like this:
var TransactionSchema = new Schema({
...
, notebooks: {type: [Notebook]}
});
var NotebookSchema = new Schema({
...
, authorName: String // this should not necessarily persist to db because it may get stale
, authorId: String
});
var AuthorSchema = new Schema({
firstName: String
, lastName: String
});
Then, in my application code (I'm using Express), when I get a Transaction, I want all of the notebooks with author last name's:
...
TransactionSchema.findById(someTransactionId, function(err, trans) {
...
if (trans) {
var authorIds = trans.notebooks.map(function(tx) {
return notebook.authorId;
});
Author.find({_id: {$in: authorIds}, [], function(err2, authors) {
for (var a in authors) {
for (var n in trans.notebooks {
if (authors[a].id == trans.notebooks[n].authorId) {
trans.notebooks[n].authorLastName = authors[a].lastName;
break;
}
}
}
...
});
This seems wildly inefficient and hacky, but I could not figure out another way to accomplish this. Lastly, I am new to node.js, mongoose, and stackoverflow so forgive me if this is not the most appropriate place to extend this discussion. It's just that Josh's solution was the most helpful in my eventual "solution."
As this is an old question, I figured it might use an update.
To achieve asynchronous virtual fields, you can use mongoose-fill, as stated in mongoose's github issue: https://github.com/Automattic/mongoose/issues/1894

Resources