Search for object on Firebase - angularjs

I'm trying to search for an object by a property with this code :
ref.orderByChild("aFieldNameOnTheFirebaseCollection").equalTo(mySearchArgument).limitToFirst(1).on("child_added", function(snapshot) {
console.log("Yes the object with the key exists !");
var thisVerificationVarisSetToTrueIndicatingThatTheObjectExists = true ;
});
This works ok if the one or more objects are found on the collection. But, I need to know if no objects exists. I could set the verification var to false before the verification but the verification process is async and I need wait until it is finished. Shoul I use a promise ?

A child_added event will fire if (and only if) a child is added. So you cannot use it to detect whether a matching child exists.
Use a value event for that:
var query = ref.orderByChild("aFieldNameOnTheFirebaseCollection").equalTo(mySearchArgument).limitToFirst(1);
query.on("value", function(snapshot) {
if (snapshot.hasChildren()) {
console.log("Yes the object with the key exists !");
var thisVerificationVarisSetToTrueIndicatingThatTheObjectExists = true ;
}
})

Related

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 retrieve parent's value based on child

I am trying to retrieve the user's key based on the child's email, which is student#memail.com in this case. I have tried many ways but could not get a way to retrieve the key of the record. I want to retrieve the value KKTxEMxrAYVSdtr0K1NH , below is the snapshot of the database
Currently, if((childSnap.val().role) == "student") { returns me student and snap.key() returns me "User". How do I retrieve KTxEMxrAYVSdtr0K1NH ?
What method are you using to retrieve the node? If you are using on "child_added" then you can use: childSnap.key
If you are using on "value" then your references is the keys in the response object. So you can use:
for (var key in childSnap.val()) {
console.log(key)
}
or
childSnap.forEach(...)
Here is an example to clearify (check the console):
https://jsfiddle.net/qrLvbok4/1/
There is a difference between value and child_added, check the list for child events section: https://firebase.google.com/docs/database/web/lists-of-data
var ref = firebase.database().ref('Users');
ref.on('child_added', function(snap) {
// You can retrieve the parent key by calling snap.key
console.log(snap.key);
});
UPDATE (Get parent key by email):
var ref = firebase.database().ref('Users').orderByChild('email').startAt('student#memail.com').endAt('student#memail.com');
ref.once('value', function(snap) {
console.log(Object.keys(snap.val())[0]);
});

How to check if value already exists?

I have a small app that users can use to search for a movie, and then add it to their watchlist. Currently it is possible to add 1 movie multple times for the same user. Which ofcourse isn't expected behaviour.
My solution would be to find the unique id of the movie that's being added and crosscheck that with my movies_users data. If the movie_id value exists, do this, else do this.
At the moment I do have the unique movie id of the movie that's being added,
$scope.movieListID = response;
console.log ($scope.movieListID.id)
Which gets ouputted like a string, like so,
314365
And I got the movie records from the current user,
$scope.moviesCheck = response.data;
console.log ($scope.moviesCheck)
Which looks like this,
[{"id":2,"title":"Black Panther", "movie_id":"284054"}]
So what would be a good way to check if the result from $scope.movieListID.id already exists in the $scope.moviesCheck data?
* update *
Trying a suggestion below does not give the expected result.
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
console.log (movie.movie_id)
console.log (id)
if (movie.movie_id === id)
console.log ('duplicate')
else
console.log ('not duplicate')
});
}
exists($scope.movieListID.id);
The console.log output from this is,
312221
312221
not duplicate
Which clearly are duplicate results.
You can add a function in your controller to check if the movie exists in the list
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
if (movie.id === id)
return true;
});
return false;
}
// and call it
exists($scope.movieListID.id); // true or false
I'm not 100% if this is a good way to do this, but for me it works and I think it's pretty low on performance,
movieService.loadMovies().then(function(response) {
$scope.moviesCheck = response.data;
var arr = $scope.moviesCheck
function myIndexOf(o) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].movie_id == o.exisitingMovie_id) {
return i;
}
}
return -1;
}
var checkDuplicate = (myIndexOf({exisitingMovie_id:movie.id}));
if (checkDuplicate == -1) {
From your question I've understood that, based on the object exists using id in the array of object, you have to do different action.
You can use $filter for this. Inject the filter for your controller and assign it to the scope. So this will be available whenever you want in this controller.
$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
$sope.movies - is the list of movies passed to the filter. You can
send any list based on your need.
{movie_id:$scope.existingMovie.movie_id} - This one is the object
which one we need to find. This can be based on your need. Since we
are searching movie_id, we need to send the object with property
and value. {movie_id:$scope.existingMovie.movie_id}, Here movie_id is
the property and followed by the value with the colon.
true: This indicates that, to search exact matched values. By default
this is false. If this is set to false, then if we want to search 54
in the movie id, this will returns the objects whichever contains 54
as part of the value.
app.controller('controller', ['$filter',function($filter){
$scope.cFilter= $filter;
$scope.Exists=function(){
$scope.movies=[{"id":2,"title":"Black Panther", "movie_id":"284054"},{"id":3,"title":"Black Panther", "movie_id":"32343"},{"id":4,"title":"Black Panther", "movie_id":"98863"}]
$scope.existingMovie={"id":3,"title":"Black Panther", "movie_id":"32343"};
var _obj=$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
if(_obj && _obj[0])
{
Console.log('object exists')
}
else
{
Console.log('Object is not found')
}
}
}])
Many Thanks Jeeva Jsb. This got me on the right track, however I thought I would clarify with a practical example that seems to work as expected.
So I have a function called getData which get the AJAX array but we need to check if the record exist before added to scope (else we get duplicates)
if (d.data.length) {
for (var i = 0; i < d.data.length; i++) {
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
if (doesExist.length == 0){
$scope.notifications.push(d.data[i]);
}
}
}
This should look familier...
when we are iterating through the returned AJAX object we need to check the ID of the (in my case notificiation)
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
This line creates a new array by filtering the existing array in scope ($scope.notifications) and passing in the same value from you interation.
If the value exists the object will be copied to the new array called doesExist.
A simple check of length will determine if the record needs to be written.
I hope this helps someone.

coordinating geoFire Ready and key_entered events

I am new to GeoFire, FireBase and Angular. I am trying to create a function that will take some coordinates and return some objects in vicinity of those coordinates.
I return a promise from the function which I assign to a scope variable used in the view hoping that when the promise is resolved by the ready event the array of objects in vicinity will be available.
obj.findGroupsInViscinity = function(pos){
var gFire = factoryAuth.geoFire;
var fbaseRef = factoryAuth.usersRef;
var groupsInQuery = {};
var groupsInQueryAr = [];
var deferred = $q.defer();
var geoQuery = gFire.query({
center: [pos.coords.latitude, pos.coords.longitude],
radius: 2
})
geoQuery.on("key_entered", function(groupId, groupLocation, distance) {
console.log("--> key_entered 1");
groupsInQuery[groupId] = true;
// Look up the vehicle's data in the Transit Open Data Set
fbaseRef.child("groups").child(groupId).once("value", function(dataSnapshot) {
console.log("--> key_entered 2");
// Get the vehicle data from the Open Data Set
group = dataSnapshot.val();
// If the vehicle has not already exited this query in the time it took to look up its data in the Open Data
// Set, add it to the map
if (group !== null && groupsInQuery[groupId] === true) {
console.log("Adding group", group);
// Add the group to the list of groups in the query
groupsInQuery[groupId] = group;
groupsInQueryAr.push({"name": group.name});
}
})
}) // end ke_entered monitoring
geoQuery.on("ready", function() {
console.log("GeoQuery ready event received. groupsInQueryAr = ", groupsInQueryAr);
deferred.resolve(groupsInQueryAr);
geoQuery.cancel();
console.log("GeoQuery canceled");
}) // Cacnel the geoQuery once we have gotten all the groups in viscinity
return deferred.promise; // Return a promise that will be resolved when ready event fires
}
Included below the console output from calling this function.
What I notice is that the key_entered part of the code is called twice in succession but before the code to process the key_entered event completes, the ready event is called because all key_entered events have fired. So while the key_entered part of the logic is building out the array I want to pass in resolving the promise, it is not ready at the time I resolve the promise in the ready event.
How can I ensure that I resolve the promise after all key_entered events have been processed and my array of objects has been built out properly?
Thanks,
Sanjay.
I would say that this is a bit of an XY problem and I would suggest you just load the restaurants into your view as you get them. This will probably be a better user experience in most cases.
That being said, if you want to do what you are asking about, you can make it work by using $.all(). Essentially, create and return your deferred promise. Start with an empty list and for every key_entered event, push a new promise onto the list. Then, in your ready event callback, do a $q.all() on the list of promises and once they complete (in the then() of the promise), do the deferred.resolve().

AngularJS watch model value if model is not null

Simple question here.
I have this watch:
// Watch our model
$scope.$watch(function () {
// Watch our team name
return self.model.team.data.name;
}, function (name) {
console.log(name);
// if we have a name
if (name) {
// Store our model in the session
sessionStorage.designer = angular.toJson(self.model);
}
});
The team model is pull in from the database as a promise (hence the data) so when the watch first fires self.model.team has not been set so it is null.
How can I get my watch to either wait until it has been set or add a check into the return function of the watch?
Use a watch expression instead of a function. This will catch any errors with missing objects and return undefined.
// Watch our model
$scope.$watch('self.model.team.data.name', function (name) {
console.log(name);
// if we have a name
if (name) {
// Store our model in the session
sessionStorage.designer = angular.toJson(self.model);
}
});
There is no magic here - if one of the variables you are accessing could be null/undefined, then you cannot get its property if it's null/undefined. So, you have to guard against that:
$scope.$watch(
function(){
return (self.model.team && self.model.team.data.name) || undefined;
},
function(v){
// ...
});
The only "magic" is when you "$watch" for expressions, but the expressions need to be exposed on the scope. So, you could do:
$scope.model = self.model;
$scope.$watch("model.team.data.name", function(v){
// ...
});
But, really, you have to ask yourself why you need a $watch here to begin with. It seems to me that you are getting the team asynchronously once - it does not look like it will change except by maybe another async call. So, just handle that when you receive the data without the $watch:
someSvc.getTeam() // I made an assumption about a service that pulls the data from db
.then(function(team){
var name = team.data.name;
// if we have a name
if (name) {
// Store our model in the session
sessionStorage.designer = angular.toJson(self.model);
}
});
An unnecessary $watch is expensive - it is evaluated on every digest cycle, so, it's best to reduce the number of $watchers.

Resources