Render an object's children from Firebase in AngularJS - angularjs

I'm following the "denormalized" data pattern that #Anant described nicely on the Firebase blog. To query and render child objects he suggests listening for child_added events on the parent to get the child ids, then querying those children individually with .once('value', fnc) to render them. From the blog post (he uses a posted Link as the parent and Comments as children -- think Reddit or Hacker News):
var commentsRef =
new Firebase("https://awesome.firebaseio-demo.com/comments");
var linkRef =
new Firebase("https://awesome.firebaseio-demo.com/links");
var linkCommentsRef = linkRef.child(LINK_ID);
linkCommentsRef.on("child_added", function(snap) {
commentsRef.child(snap.name()).once("value", function() {
// Render the comment on the link page.
));
});
[sic]
I'm trying to reconcile this with my AngularJS view, since adding the result of .once into a $scope array and using ngRepeat will leave you with a static list (the children won't update in realtime if they are changed or removed by another client).
Put another way, I'd like to have something like an angularFireCollection of child objects that will add, remove and update dynamically.

I think what you're looking for is a combination of AngularFire and FirebaseIndex. I haven't checked that combining these still works, but Kato reports it has in the past.
Ignoring that for a second, though, I don't see anything wrong with your proposed plan:
push the result of .once into an array, add it to the $scope and use ngRepeat
In this case, Kato's FirebaseIndex will probably still be useful, so definitely check that out.

Related

AngularFire - Get a firebaseObject for each item in a firebaseArray ng-repeat

I have an ng-repeat which is connected to a firebaseArray which is an index of a user's doc Ids.
I want to display a list of their docs with additional info for each one, e.g. it's title and description.
My firebase structure is:
+users
- userId1
-theirDocs
-docId1: true
-docId2: true
+docs
-docId1
- tile
- description
- url
-docId2
- etc etc
I've tried everything and still can't get this to work, it seems a pretty common use case. I've tried using a function that calls a firebaseObject each time. I've tried using a separate controller. I've tried a directive, I've even tried using firebase.utils library (I think out of date now).
Can anyone recommend the best way to do this using AngularFire? Thanks in advance!
It would be better to see a code snippet as to fully understand what you've tried first of all, as it's difficult to determine where the issues lie. That being said, as long as your rules are not preventing this, you may need to wait for the data to download using $loaded function beforehand.
EXAMPLE
This example is clearly available by following this link, yet here is an example for further clarity:
var yourFirebaseReference = firebase.database().ref("REPLACE WITH YOUR FIREBASE BRANCH HERE");
var data = $firebaseArray(yourFirebaseReference);
data.$loaded().then(function(x) {
console.log(data) // Example, you can do what you want with your 'data' now as the data has loaded
x === data; // true
}).catch(function(error) {
console.log("Error:", error);
});

Prevent Firebase $firebaseArray auto update

I am working on a News Feed that is similar to Facebook News Feed. I am implementing it in the following sequence:
When app is started, get everything, but only push two item to show.
The user can scroll down to see more and I will add more to the list.
If there are any new items, the user needs to pull to refresh to add the new ones.
The only problem I have right now is that each time an item is added, $firebaseArray automatically adds the new item to my array that I do not want snyc changes (I want to query once, get a set list). I was wondering if anyone would know how to prevent the auto update?
Look into .once this will allow you to get the data just once: https://firebase.google.com/docs/reference/js/firebase.database.Query#once
Instead of binding directly your $firebaseArray to the view, clone it to a scope (or controller) property that will be bound to the view. Factorize this process in a function you can call when the user pull-reloads so the the $firebaseArray is re-created.
A more appropriate approach would be to use Firebase's http api, with the angular $http service.
Edit: to clone the array to a scope var, try the following:
var allEvents = $firebaseArray(queryOfeverything);
allEvents.$loaded()
.then(function(events){
$scope.allEvents = angular.map(events, function(event) {
return event; // you may want to replace this line - I am not very familiar with firebase.
})
});

Angular CRUD, update view when backend/database changes ($resource and REST)

I am currently making an application in angular which does this:
(On page load) Make an api call in angular controller (to symfony2 end point) to get: items.
$scope.items = ItemsService.query(function(data){
$scope.loading = false;
}, function(err){
$scope.loading = false;
});
items is an array containing many item objects.
Each item contains parameters e.g. item.param1 item.param2.
I have built it in a similar way to this tutorial:
http://www.sitepoint.com/creating-crud-app-minutes-angulars-resource/
i.e. The angular controller calls a service which calls the (symfony2) backend api endpoint.
The endpoint passes back items which is gets from a database. Items are then put into the view using ng-repeat (item in items).
This all works fine.
Now, I have a button (in the ng-repeat) which effectively causes a PUT request to be made to (another symfony2 endpoint), thus updating item.param1in the database. This also happens in the tutorial I linked to.
The problem is that (in my application and in the tutorial) I have to again make an api call which updates ALL the items, in order to see the change.
I want to just update the data in the view (immediately) for one object without having to fetch them all again.
i.e. something like:
$scope.items[4] = Items.get({id: item.id}, function(){});
Except the application array key isn't known so I cant do that.
(so something like: $scope.items.findTheOriginalItem(item) = Items.get({id: item.id}, function(){});.
Another possible solution (which seems like it may be the best?). I have looked here:
http://teropa.info/blog/2014/01/26/the-three-watch-depths-of-angularjs.html
And tried doing the equality $watch: $scope.$watch(…, …, true);. Thus using watch to see when the item sub-array is updated. This doesn't seem to update the data in the view though (even though it is updating in the database).
Any advice on the best way of doing this (and how to do it) would be great! Thanks!
Essentially the button click should use ng-click to execute a function and pass the item to that function. Example:
...ng-repeat="item in items"...
<button type="button" ng-click="updateItem(item)">Update</button
...
Then in the function you have the exact item that you want to update. If you are using $resources, it would be something like:
$scope.updateItem = function(item) { item.$update(...); };
Unless I didn't understand you

How to synchronize object with children arrays using AngularFire

Hello fellow Angular gurus!
I have a simple website with some galleries. The gallery names and content are saved in Firebase along with few other data.
I would like to synchronize the whole tree with Angular to allow admins to edit anything on the page.
This is how I sync it and it mostly works ...
var ref = new Firebase("https://profile-web.firebaseio.com/");
// create an AngularFire reference to the data
var sync = $firebase(ref);
// download the data into a local object
var syncObject = sync.$asObject();
// synchronize the object with a three-way data binding
syncObject.$bindTo($scope, "data");
... mostly.
What doesn't work is updating data. I think it's because the tree contains objects with arrays as children. AngularFire requires to use $asArray() to work with arrays. It filters out $$hashkey that gets added by Angular's ng-repeat and breaks the update process. $asObject() doesn't do that.
I tried binding the whole tree with $asArray() but since it's not an array it fails.
So my question: How do I sync the whole tree that is an object with arrays as children that have arrays as children themselves?
I have a working copy here
http://tr.tomasreichmann.cz/
Your time and effort is greatly appreciated. Thank you!

How do I iterate a Backbone Firebase Collection?

I currently started using Firebase as my backend solution for persistance.
I found it easy to create new objects and persist it to Firebase with Backfire with a simple
collection.add(obj)
The issue comes when I try to get a Firebase collection from the server.
For example when i try
console.log(collection);
I get this output:
=> {length: 0, models: Array[0], _byId: Object, _events: Object, constructor: function…}
Which result in an empty models array
console.log(collection.models);
=> []
After some searching, I figured out that Backbone Collections aren't yet loaded at the time I try to log it to the console (see this previous question).
I also tried using
Backbone.Collection.extend({
model: Todo,
firebase: new Backbone.Firebase("https://<your-namespace>.firebaseio.com")
});
To explicitly call fetch from the server and use success callback with no success either.
My question is: How can I get the Firebase Collection and populate the DOM from it?
When you call Backbone.Firebase.Collection.add, it does not get added to the collection synchronously. Rather, it sends the request to Firebase and then waits for the return event. See the code here
Thus, if you immediately try to read the collection, you will see zero elements. However, if you try something like this:
collection.once('add', function() { console.log(collection.length); });
You'll see the element you have added.
Remember that we're dealing with real-time data here, so when you want to populate the DOM, you shouldn't think in terms of a single transaction, but instead rely on events and take everything as you get it (in real time).
So to populate the DOM, do something like this in your view:
Backbone.View.extend({
render: function() {
this.listenTo(this.collection, 'add', this.rowAdded);
},
rowAdded: function(m) {
/* use `m` here to create your new DOM element */
}
});
Additionally, you'll probably want to check out a nice binding library like ModelBinder to help you deal with the constantly changing DOM, so you don't have to re-invent any wheels.
It seems you have to use a Backbone.Firebase.Collection and not a Backbone.Collection which will tell you that your calls to fetch or sync are silently ignored.
Also, Backbone.Firebase's got a read and a readall methods that should get you started. It seems Backbone.Firebase.Collection doesn't inherit this method, but I'm not sure though.
Edit:
As Kato stated in his comment, it seems you don't have to do anything. Just use Backbone.Backfire.Collection and Backbone.Backfire.Model.

Resources