Retrieving and rendering data from Firebase in Angular - angularjs

I am using Angular to add data (Users and their Orders) to Firebase.
Below is the DB structure in Firebase.
Firebase DB Structure
Below is the code I am using to RETRIEVE all the Orders for a particular User from Firebase.
var orddataref = fbref.child("profile").child($scope.cart.userkey).child("orders");
orddataref.on("child_added", function(snap) {
ordersRef.child(snap.key()).once("value", function(snapshot) {
$scope.odata = snapshot.val();
$scope.apply();
});
});
Below is the code I am using to RENDER in my HTML.
<div ng-repeat="order in odata">
<div> Order Address : {{order.address}}</div>
<div> Deliverynotes : {{order.deliverynotes}}</div>
<div> Total Amount : {{order.totalamount}}</div>
<ion-item class ="item" ng-repeat="item in order.items">
<div> Item SKU : {{item.sku}}</div>
<div> Price : {{item.price}}</div>
<div> Quantity : {{item.quantity}}</div>
</ion-item>
</div>
It is getting only current item back and not all the orders (including previous orders).
Can anyone suggest where I am going wrong? Any help is appreciated very much.
Edited after Frank's suggestion
Frank, I put a breakpoint on the line $scope.odata = snapshot.val(); It is just having the current order as below.
{"address":"Auckland, New Zealand","deliverynotes":"At door","items":[{"price":3,"quantity":2,"sku":"1002"},{"price":6.5,"quantity":3,"sku":"2002"}],"totalamount":25.5}
So I put a logger after it.
console.log("displayOrders - orderdata :===", $scope.odata);
Below is a screengrab of chrome console where I see all the orders.
All Orders
How do I get all these orders into $scope.odata?

Just cracked it.
Below is the correct code for getting all orders for a particular user.
Please refer to the Database structure in the question if you need to.
$scope.odata = {};
$scope.tempdata =[];
var fbref = new Firebase("https://xxxxxxx.firebaseio.com/");
var ordersRef = fbref.child("orders");
var orddataref = fbref.child("profile").child($scope.cart.userkey).child("orders");
orddataref.on("child_added", function(snap) {
ordersRef.child(snap.key()).once("value", function(snapshot){
$scope.tempdata.push(JSON.parse(JSON.stringify(snapshot.val())));
});
});
$scope.odata = $scope.tempdata;
Since I am a newbie, if anyone has a better solution, please suggest.
#Frank, thank you for pointing to the right path.

Related

Mapping firebase data in ionic list

I am storing my data in firebase with update() like so
var newKey = firebase.database().ref().push().key;
var updates = {};
updates['metadata/' + newKey] = {
name: $scope.formData.name,
price: $scope.formData.price
};
updates['realdata/' + newKey] = {
name: $scope.formData.name,
price: $scope.formData.price,
date: $scope.formData.date
};
return firebase.database().ref().update(updates)
.then(function(ref){
console.log("added in fb");
}, function(error){
console.log("error " + error)
});
Now on an other page I am pulling the data out of firebase, but I can't seem to map it to my list in my view.
I tried multiple ways to pull the data out and in both ways I can see the data when logging it to the console.
var dbRef = firebase.database().ref('/metadata');
//Method 1
$scope.list = $firebaseArray(dbRef);
/*
Result here in the console is an array with objects
but when setting this in my list, I get the same amount of items pulled out but they are empty
*/
//Method 2 - I prefer this way as per docs it's a best practice
var loadmetadata = function(data){
console.log(data.val().name); // I get the actual name
$scope.list = data.val().name
};
dbRef.on('child_added', loadmetadata);
dbRef.on('child_changed', loadmetadata);
My view is just a simple
<ion-item ng-repeat="listitem in list">
{{ listItem.name }}
</ion-item>
What am I missing? I prefer the second method, if someone can help me achieve this?
The thing is I've found someone with the same problem here on SO, and he was able to solve it with the methods I have above. Here is the link to the question/answer Firebase 3 get list which contain generated keys to ionic list
The only difference I am seeing is that he's sorting the results, but I don't need that currently.
I've just figured it out! Instead of using $firebaseArray, I need to use the $firebseObject method.
$scope.metadataObj = $firebaseObject(dbRef);
and in my view I can do the following:
<ion-item ng-repeat="(key, value) in metadataObj">{{value.name}}</ion-item>
This method contains all the child methodes too. So no need to listen for them separetely.

Accessing Related Object Data in AngularFire for Firebase

I want to relate a business to another business in my Firebase data and then use AngularFire to ng-repeat through the related businesses. My Firebase data is structured like so:
{
"businesses" : {
"-KQ1ggyrnYGYL1084Htz" : {
"name" : "Some Business",
"cat" : "Food",
"related" : {
"-KQ1hFH0qrvKSrymmn9d" : true,
In my JS I have the following factory and controller:
app.factory("Businesses", ['$firebaseArray', function ($firebaseArray) {
var stuff = firebase.database().ref().child("businesses");
var Businesses = $firebaseArray(stuff);
return Businesses;
}])
And my controller:
app.controller('businessCtrl', ['$scope','$firebaseArray','Businesses', function ($scope, $firebaseArray, Businesses) {
$scope.businesses = Businesses;
In the HTML I then loop through the "Related" businesses using an ng-repeat like so:
<span>Related:</span>
<a ng-repeat="item in businesses.related" ui-sref="shop.item({item: item.link})">
<div class="chip">
<img ng-src="{{item.card}}" alt="Contact Person">
{{item.name}}
</div>
</a>
But I seem unable to access any data such as "name" from my {{item.name}}. What do I need to do to be able to access the data of the referenced business?
If you want to access the data item.name then use item in businesses instead of item in businesses.related
When using item in businesses.related you are getting the data as item.-KQ1hFH0qrvKSrymmn9d
Alright, so I discovered how to do this sometime later. It may have worked the way I was trying, but it's not the recommended approach. For one, denormalization in Firebase is normal. Meaning, instead of placing the related businesses in a sub-object I need to create a new object like so:
businesses
-BusinessKey
related
-BusinessKey
-RelatedBusinessKey
Then get the data like so:
$scope.related = [];
const businessKey = $scope.finalItem.$id;
const rootRef = firebase.database().ref();
const discoverRef = rootRef.child('discover');
const businessRef = rootRef.child('businesses');
function getRelatedContent(key, cb) {
relatedRef.child(key).on('child_added', snap => {
let businesRef = businessRef.child(snap.key);
businesRef.once('value', cb);
});
}
getRelatedContent(businessKey, snap => {
var snapVal = snap.val();
$scope.related.push(snapVal);
});

Filter data by user with angularfire

I'm using angular-ui-fullcalendar to show and edit events. Users can log in and have unique uid when logged in. I want to use this to distinguish events made by current user from other events. I want to give current user events another backgroundColor.
What is the best way to do this??
I tried several things. My data looks like this:
```
database
bookings
-KWnAYjnYEAeErpvGg0-
end: "2016-11-16T12:00:00"
start: "2016-11-16T10:00:00"
stick: true
title: "Brugernavn Her"
uid: "1f17fc37-2a28-4c24-8526-3882f59849e9"
```
I tried to filter all data with current user uid like this
var ref = firebase.database().ref().child("bookings");
var query = ref.orderByChild("uid").equalTo(currentAuth.uid);
var bookings = $firebaseArray(query);
$scope.eventSources = [bookings];
This doesn't return anything. If I omit the filter in line 2 it returns all bookings as expected. But even if the filter worked it would not solve my problem, because I want to fetch both current user events and all other events. Firebase does not have a "not equal to" filter option...
I tried to loop through each record and compare uids and setting backgroundColor if condition was met:
var ref = firebase.database().ref().child("bookings");
var bookings = $firebaseArray(ref);
bookings.$ref().on("value", function(snapshot) {
var list = snapshot.val();
for (var obj in list) {
if ( !list.hasOwnProperty(obj) ) continue;
var b = list[obj];
if (b.uid === currentAuth.uid) {
b.className = "myBooking";
b.backgroundColor = "red";
}
}
});
$scope.eventSources = [bookings];
But this causes asynchronous problems so the 'bookings' array assigned to $scope.eventSources wasn't modified. I tried to move the $scope.eventSources = [bookings] inside the async code block but FullCalendar apparently can't handle that and renders nothing.
I also tried this but no luck either:
bookings.$loaded()
.then(function(data) {
$scope.eventSources = [data];
})
.catch(function(error) {
console.log("Error:", error);
});
What is the best solution to my problem?
If you're looking to modify the data that is loaded/synchronized from Firebase, you should extend the $firebaseArray service. Doing this through $loaded() is wrong, since that will only trigger for initial data.
See the AngularFire documentation on Extending $firebaseArray and Kato's answer on Joining data between paths based on id using AngularFire for examples.

How to append a new value to an item within an array in Firebase?

Within Firebase, I have a list of 'ideas.' If a user presses a button associated with the idea, I'd like a value to be appended to that idea under an attribute called 'newValue.'
For example, the below html, uses ng-repeat to show the array of ideas and creates an associated button called 'Append Value.' I want a new value to be appended to the idea's attribute called 'newValue' every time a user presses 'Append Value.'
<body ng-controller="ctrl">
<table>
<tr class="item" ng-repeat="(id,item) in ideas">
<td>{{item.idea}}</td>
<td><input ng-model="newValue"></td>
<td><button ng-click="ValueAppend(id,newValue)">Append Value</button></td>
</tr>
</table>
</body>
Below is my attempt to create this function.
var app = angular.module("app", ["firebase"]);
app.factory("Ideas", ["$firebase", function($firebase) {
var Ref = new Firebase('https://crowdfluttr.firebaseio.com/');
var childRef = Ref.child('ideas');
return $firebase(childRef).$asArray();
}]);
app.controller("ctrl", ["$scope","Ideas", function($scope,Ideas) {
$scope.ideas = Ideas;
$scope.idea = "";
$scope.ValueAppend = function (id,newValue) {
var URL = "https://crowdfluttr.firebaseio.com/ideas/" + id + "newValue";
var IdeaRef = new Firebase(URL);
var IdeaData = $firebase(IdeaRef);
$scope.IdeaAttributes = IdeaData.$asArray();
$scope.IdeaAttributes.$add({
newValue: newValue,
timestamp: Date.now()
});
};
}]);
See my codepen for my working example: http://codepen.io/chriscruz/pen/PwZWKG
More Notes:
I understnad that AngularFire provides $add() and $save() to modify this array, but how could I use these methods so that I can add a new 'string' under an item in an array.
I'm not sure if these are your problems, but they are two typoes of mistakes in the code above and the codepen: typos and conceptual.
Typos
You forgot to inject $firebase into the controller, which leads to:
"ReferenceError: $firebase is not defined"
Solution is simply of course:
app.controller("ctrl", ["$scope","Ideas", "$firebase", function($scope,Ideas,$firebase) {
In addition you seem to be missing a slash before newValue, which means that you're trying to create a new idea instead of adding the value to an existing one. Solution is simple again, add a slash before newIdea as in:
var URL = "https://crowdfluttr.firebaseio.com/ideas/" + id + "/newValue";
If you find yourself making this mistake more often, you might be better server by the child function. Although it typically is a bit more code, it lends itself less to this typo of typo. Creating the ref to the newValue node becomes:
var URL = "https://crowdfluttr.firebaseio.com/ideas/";
var IdeaRef = new Firebase(URL).child(id).child("newValue");
Conceptual
With those trivial typos out of the way, we can focus on the real problem: which is easiest to see if you console.log the URL that you generate:
https://crowdfluttr.firebaseio.com/ideas/0/newValue
Yet if you look up the same data in the Firebase forge (by going to https://crowdfluttr.firebaseio.com/ideas/ in your browser), you'll see that the correct URL is:
https://crowdfluttr.firebaseio.com/ideas/-JbSSmv_rJufUKukdZ5c/newValue
That '0' that you're using comes from the id and it is the index of the idea in the AngularJS array. But it is not the key that Firebase uses for this idea. When AngularFire loads your data with $asArray it maps the Firebase keys to Angular indexes. We need to perform the reverse operation to write the new value to the idea: we need to map the array index (in id) back to the Firebase key. For that you can call [$keyAt(id)][1]. Since you keep the array of ideas in Ideas, it is simply:
var URL = "https://crowdfluttr.firebaseio.com/ideas/";
var IdeaRef = new Firebase(URL).child(Ideas.$keyAt(id)).child("newValue");
So the controller now becomes:
app.controller("ctrl", ["$scope","Ideas", function($scope,Ideas) {
$scope.ideas = Ideas;
$scope.idea = "";
$scope.ValueAppend = function (id,newValue) {
var URL = "https://crowdfluttr.firebaseio.com/ideas/";
var IdeaRef = new Firebase(URL).child(Ideas.$keyAt(id)).child("newValue");
var IdeaData = $firebase(IdeaRef);
$scope.IdeaAttributes = IdeaData.$asArray();
$scope.IdeaAttributes.$add({
newValue: newValue,
timestamp: Date.now()
});
};
}]);
I quickly gave it a spin in your codepen and this seems to work.

Displaying two tables within a loop

I'm pretty new to AngularJS.
I'm using it with firebase.
I have a set of data like this one:
"userId1" : {
"name" : "Bobby",
"comments" : {
"-J_v90hh5sbJ2acmX4nq" : true
}
}
and a comment list
"-J_v90hh5sbJ2acmX4nq" : {
"creation_date" : 1415110466568,
"creator_id" : "userId1",
"desc" : "dsqdsq",
"type" : "suggest"
}
So what I want to do is to display all the comments of our friend Bobby.
.controller('CommentsCtrl', function($scope, $firebase){
var creator_id = 'userId1'; // Test purpose
var userCommentsRef = new Firebase("https://awesome.firebaseio.com/users/"+creator_id+'/comments');
var commentsRef = new Firebase("https://awesome.firebaseio.com/comments");
var userCommentsSync = $firebase(userCommentsRef);
var commentsSync = $firebase(commentsRef);
var userComments = userCommentsSync.$asObject();
var comments = commentsSync.$asObject();
$scope.userComments = userComments;
$scope.comments = comments;
})
Do you know how I should proceed with this display, but to be sure that if the comments has been deleted (and the key is still there), the description will not be displayed?
<ion-list ng-controller="CommentsCtrl">
<ul class="list">
<li class="item item-checkbox item-checkbox-right" ng-repeat="(key,item) in userComments">
{{comments[key].desc}} // Working, but don't want deleted comments
</li>
</ul>
</ion-list>
Is this the correct way to handle this situation?
How can I say to my controller to display only existing comments ?
You can use filter in ng-repeat block in a such way:
<li ng-repeat="(key,item) in userComments | filter:{deleted:false}">
{{comments[key].desc}} // Working, but don't want deleted comments
</li>
or, if information stored in comments object use ng-if:
<li ng-repeat="(key,item) in userComments | filter:{deleted:false}" ng-if="!comments[key].deleted">
{{comments[key].desc}} // Working, but don't want deleted comments
</li>
No, this is not the correct way. When working with collections, utilize $asArray instead of $asObject. From the guide:
Synchronized arrays should be used for any list of objects that will be sorted, iterated, and which have unique ids.
Objects are useful for storing key/value pairs, and singular records that are not used as a collection.
Also, there is no need to have a deleted parameter. You could simply removed the deleted comments from the user's index. Then if it exists in that list, it is not deleted. There is no need for extra filtering fanciness.
That said, you may also want to check out the new query parameters. With those, and a slight data structure change, you could match on items with a property of deleted equal to false, rather than sorting client-side in Angular.
So all together:
var fbRef = new Firebase("https://awesome.firebaseio.com/");
var userCommentsRef =
fbRef.child("users" + creator_id + "/comments")
.orderBy('deleted')
.startAt(false)
.endAt(false);
$scope.userComments = $firebase(userCommentsRef).$asArray();

Resources