Firebase .once(): Use success and failure callbacks - angularjs

I have the following function in my Angular-app
$scope.retrieveProjectData = function() {
$scope.projectNumberNoChange = false;
// Only retrieve Data if the ProjectNumber changed
if (currentlySelectedProjectNumber != $scope.feedback.projectNumber.content) {
currentlySelectedProjectNumber = $scope.feedback.projectNumber.content;
// Go to database-reference based on the projectNumber
var projectsRef = firebaseDatabaseRef.child("projects");
var currentChild = projectsRef.child(currentlySelectedProjectNumber);
// retrieve data once and fill $scope.feedback
currentChild.once("value",
// If the project is found
function (dataSnapshot) {
// Fill selectedProject and hand over to writeDataFromSelectedProject()
var selectedProject = dataSnapshot.val();
// Fill $scope.feedback
writeDataFromSelectedProject(selectedProject);
},
// If no data is found
function () {
console.log("No data found");
});
}
// If the projectNumber didn't change, the projectNumberNoChangeMessage will be shown
else {
$scope.projectNumberNoChange = true;
}
};
The user has the possibility to load some data regarding his project-number (for instance: Name, email, tel) to make it faster for the user to fill a form.
In the part:
currentChild.once("value",
// If the project is found
function (dataSnapshot) {
// Fill selectedProject and hand over to writeDataFromSelectedProject()
var selectedProject = dataSnapshot.val();
// Fill $scope.feedback
writeDataFromSelectedProject(selectedProject);
},
// If no data is found
function () {
console.log("No data found");
});
only the first Callback-function is called, even if the projectNumber was not found. How can I use the "failureCallbackOrContext" as described in the docs?
Thanks for taking the time!

The problem was solved. I just checked the dataSnapshot.val() for beeing an object or null!

Related

AngularJS: Passing Promise, yet Cannot read property 'finally' of undefined

In my angular app, I have 2 methods save() and saveTriggers(). saveTriggers() updates all records by calling a web service (C#). I want to make sure that a block of code is executed after all records are updated in saveTriggers() and control is returned to save(). I believe I need to pass something from the saveTriggers() to make finally block execute. I tried various things, nothing works. Using .then() also gives the same error. I am not that good at JS. Can you please guide me.
vm.updatedTriggers = []; // IDs are pushed in
vm.saveTriggers = function () {
if (vm.updatedTriggers.length === 0) {
vm.close();
} else {
vm.saving = true;
vm.save()
.finally(function () { // ERROR - Cannot read property 'finally' of undefined
console.log("Saved all. Closing..."); // Never REACHES here
vm.saving = false;
vm.updated = true;
$uibModalInstance.close(true);
});
}
};
vm.save = function () {
//vm.saving = true;
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
target: trigger.target,
targetInfo: vm.targetData,
event: trigger.event,
eventQuantity: trigger.eventQuantity,
eventQuantityExtra: trigger.eventQuantityExtra
};
rpmService.editDeviceTrigger(input);
/*.finally(function () {
console.log("Updated event"); // Reaches here
vm.updated = true;
return Promise.resolve(2);
});*/ // Commenting this also doesn't help
}
return Promise.resolve(2);
};
rpmService.editDeviceTrigger(input)
public async Task EditDeviceTrigger(EditDeviceTriggerInput input) {
// calls other methods with await
// Doesn't return anything
}
EDIT: Updated Code: I got rid of the error, but the output is not is expected series.
vm.saveTriggers = function () {
vm.saving = true;
vm.save().then
(function success() {
console.log("Returned Result ");
console.log("Saved all. Closing..."); // These lines are executed before the event is upated
vm.saving = false;
$uibModalInstance.close(true);
});
};
vm.save = function () {
var deferred = $q.defer();
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
....
};
rpmService.editDeviceTrigger(input)
.finally(function () {
console.log("Updated event"); // Successfully updates all events
vm.updated = true;
});
}
deferred.resolve();
return deferred.promise;
};
OUTPUT:
Returned Result
Saved all. Closing...
Updated event
EXPECTED OUTPUT:
Updated event
Returned Result
Saved all. Closing...
Thanks.
Usually you dont need $q.defer-related things, but u can do same using it if u want.
Here I guess you just need to collect all your save promises and return new resulting one using $q.all:
vm.save = function () {
const myAwesomePromises = []
for (var i = 0; i < vm.updatedTriggers.length; i++) {
...
const savePromise = rpmService.editDeviceTrigger(input);
savePromise.finally(() => console.log('edit device finally'));// <-- not sure u need this
myAwesomePromises.push(savePromise);
}
return $q.all(myAwesomePromises).finally(() => console.log('All edit device finally'));
};

Extending $firebaseArray with an extended $firebaseObject

Trying to cut down code repetition, I've set up a $firebaseArray extension as follows:
var listUsersFactory = $firebaseArray.$extend({
$$added: function (snap) {
return new Customer(snap);
},
$$updated: function (snap) {
var c = this.$getRecord(snap.key);
var updated = c.updated(snap);
return updated;
},
});
and the Customer code:
function Customer(snap) {
this.$id = snap.key;
this.updated(snap);
}
Customer.prototype = {
updated: function(snap) {
var oldData = angular.extend({}, this.data);
this.data = snap.val();
// checks and such
}
}
This works wonders when loading, showing and saving a list of customers, and I'm satisfied with it.
Now, the problem lies in retrieving a single customer and its detail page, because the Customer object isn't an extension of $fireObject and is therefore lacking a $save method
Single customer loading:
customersRef.once("value", function(snapshot) {
if(snapshot.child(uuid).exists())
{
customersFactory.customerDetails = new Customer(snapshot.child(uuid));
return deferred.resolve();
}
}
but when I call customersFactory.customerDetails.$save() I get an error
How can I extend my class so that it works for both array and single object uses?
I couldn't find a way to do this, so I ended up using the $firebaseArray and getting single records off that to pass as details, in case anyone's wondering

Angular template won't load. Even with $loaded. Data resolves after Load

Using AngularFire, Angular, Firebase.
I load a list of users from a Firebase Database. I use $loaded to ensure it waits until data loads.
I take this list, compare it against another firebase database of groups and push the results into two arrays.
Based on the console.logs the data sorts correctly. However, inside my template I get a blank page (I think this is because the page loads before the data is sorted).
Thoughts?
let userLoggedIn = AuthFactory.getUser();
var allUsersArray = $firebaseArray(ConnectFactory.fbUserDb);
var x = firebase.database().ref('groups');
var friendArr = [];
var notFriendArr = [];
allUsersArray.$loaded().then(function(){
angular.forEach(allUsersArray, function(user, i) {
var haveIAdded = x.child(userLoggedIn).child(allUsersArray[i].uid).once('value').then(function (snap) {
if (snap.val() !== null) {
return true;
} else {
return false;
}
});
var haveTheyAdded = x.child(allUsersArray[i].uid).child(userLoggedIn).once('value').then(function (snap) {
if (snap.val() !== null) {
return true;
} else {
return false;
}
});
Promise.all([haveIAdded, haveTheyAdded]).then(function([you, they]) {
if (you && they) {
console.log('We Are Friends', allUsersArray[i]);
friendArr.push(allUsersArray[i]);
} else {
console.log('not a friend ', allUsersArray[i]);
notFriendArr.push(allUsersArray[i]);
}
});
});
$scope.friendList = friendArr;
$scope.notFriendList = notFriendArr;
});
Alright, this time I tried to actually read the question before attempting to answer. ;-)
When you set your $scope.friendList and $scope.notFriendList within the $loaded promise, your Promise.all may (and most likely) havn't resolved yet when those are called, since angular.forEach doesn't wait for the promises to finish before moving on to the next statement in the function. So you'll have to build an array of promises and wait for them all to resolve outside of the loop before attempting to set your $scope variables.
allUsersArray.$loaded().then(function(){
var promises = [];
var friendArr = [];
var notFriendArr = [];
angular.forEach(allUsersArray, function(user, i) {
... // Same as before
promises.push(
Promise.all([haveIAdded, haveTheyAdded]).then(function([you, they]) {
if (you && they) {
console.log('We Are Friends', allUsersArray[i]);
friendArr.push(allUsersArray[i]);
} else {
console.log('not a friend ', allUsersArray[i]);
notFriendArr.push(allUsersArray[i]);
}
})
);
});
Promise.all(promises).then(function(){
$scope.friendList = friendArr;
$scope.notFriendList = notFriendArr;
});
});

Add variable to an existing json object

request = myService.getCases();
request.then(
function(payload) {
$scope.cases = payload.data;
var time = Math.floor((Date.now() - Date.parse($scope.cases[i].date_case_modified))/(60000*60*24));
$scope.cases.duration.push(time);
}
});
Inside the controller I am trying to tack on the cases.duration onto the cases object but it wont add it onto the object that is returned. Any ideas?
I think you just need to introduce a forEach as shown here:
request = myService.getCases();
request.then(
function(payload) {
$scope.cases = payload.data;
angular.forEach($scope.cases, function (el) {
var time = Math.floor((Date.now() - Date.parse(el.date_case_modified))/(60000*60*24));
el.duration = time;
});
}
});
Hope this helps

accessing items in firebase

I'm trying to learn firebase/angularjs by extending an app to use firebase as the backend.
My forge looks like this
.
In my program I have binded firebaseio.com/projects to $scope.projects.
How do I access the children?
Why doesn't $scope.projects.getIndex() return the keys to the children?
I know the items are in $scope.projects because I can see them if I do console.log($scope.projects)
app.js
angular.module('todo', ['ionic', 'firebase'])
/**
* The Projects factory handles saving and loading projects
* from localStorage, and also lets us save and load the
* last active project index.
*/
.factory('Projects', function() {
return {
all: function () {
var projectString = window.localStorage['projects'];
if(projectString) {
return angular.fromJson(projectString);
}
return [];
},
// just saves all the projects everytime
save: function(projects) {
window.localStorage['projects'] = angular.toJson(projects);
},
newProject: function(projectTitle) {
// Add a new project
return {
title: projectTitle,
tasks: []
};
},
getLastActiveIndex: function () {
return parseInt(window.localStorage['lastActiveProject']) || 0;
},
setLastActiveIndex: function (index) {
window.localStorage['lastActiveProject'] = index;
}
}
})
.controller('TodoCtrl', function($scope, $timeout, $ionicModal, Projects, $firebase) {
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
var keys = $scope.projects.$getIndex();
console.log($scope.projects.$child('-JGTmBu4aeToOSGmgCo1'));
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("" + keys[0]);
});
// A utility function for creating a new project
// with the given projectTitle
var createProject = function(projectTitle) {
var newProject = Projects.newProject(projectTitle);
$scope.projects.$add(newProject);
Projects.save($scope.projects);
$scope.selectProject(newProject, $scope.projects.length-1);
};
// Called to create a new project
$scope.newProject = function() {
var projectTitle = prompt('Project name');
if(projectTitle) {
createProject(projectTitle);
}
};
// Called to select the given project
$scope.selectProject = function(project, index) {
$scope.activeProject = project;
Projects.setLastActiveIndex(index);
$scope.sideMenuController.close();
};
// Create our modal
$ionicModal.fromTemplateUrl('new-task.html', function(modal) {
$scope.taskModal = modal;
}, {
scope: $scope
});
$scope.createTask = function(task) {
if(!$scope.activeProject || !task) {
return;
}
console.log($scope.activeProject.task);
$scope.activeProject.task.$add({
title: task.title
});
$scope.taskModal.hide();
// Inefficient, but save all the projects
Projects.save($scope.projects);
task.title = "";
};
$scope.newTask = function() {
$scope.taskModal.show();
};
$scope.closeNewTask = function() {
$scope.taskModal.hide();
};
$scope.toggleProjects = function() {
$scope.sideMenuController.toggleLeft();
};
// Try to create the first project, make sure to defer
// this by using $timeout so everything is initialized
// properly
$timeout(function() {
if($scope.projects.length == 0) {
while(true) {
var projectTitle = prompt('Your first project title:');
if(projectTitle) {
createProject(projectTitle);
break;
}
}
}
});
});
I'm interested in the objects at the bottom
console.log($scope.projects)
Update
After digging around it seems I may be accessing the data incorrectly. https://www.firebase.com/docs/reading-data.html
Here's my new approach
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
projectRef.on('value', function(snapshot) {
if(snapshot.val() === null) {
console.log('location does not exist');
} else {
console.log(snapshot.val()['-JGTdgGAfq7dqBpSk2ls']);
}
});
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("a");
});
I'm still not sure how to traverse the keys programmatically but I feel I'm getting close
It's an object containing more objects, loop it with for in:
for (var key in $scope.projects) {
if ($scope.projects.hasOwnProperty(key)) {
console.log("The key is: " + key);
console.log("The value is: " + $scope.projects[key]);
}
}
ok so val() returns an object. In order to traverse all the children of projects I do
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
projectRef.on('value', function(snapshot) {
if(snapshot.val() === null) {
console.log('location does not exist');
} else {
var keys = Object.keys(snapshot.val());
console.log(snapshot.val()[keys[0]]);
}
});
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("a");
});
Note the var keys = Object.keys() gets all the keys at firebaseio.com/projects then you can get the first child by doing snapshot.val()[keys[0])

Resources