How to update several nodes in firebase using then - angularjs

I want to update several nodes in firebase since data is denormalized. So when updating each node have to wait until the previous updates is success.
So I have something below (what I currently use), you can see it is not readable if getting more nodes to update.
if(foo1) {
firebaseRef.update(data, function(error) {
if(error){
console.log(error);
} else {
firebaseRef.update(data, function(error){
if(error){
console.log(error);
}else{
//Update Another
};
});
};
});
};
So any idea how to use .then in angularFire or pure firebase js api something like this.
firebaseRef().then(function(){
return doSomething;
}).then(function(){
return doSomething;
}).then(function(){
return doSomething;
}).then(function(){
return doSomething;
});

There are no futures in Firebase or AngularFire, at present. So you would need to use a decorator/wrapper strategy.
jQuery:
function update(ref, data) {
$.Deferred(function(def) {
ref.update(data, function(err) {
if( err ) def.reject(err);
else def.resolve();
});
});
}
var fb = new Firebase(URL);
update(fb.child('path1'), 'foo')
.then(update.bind(null, fb.child('path2'), 'bar'))
.then(update.bind(null, fb.child('path3'), 'foobar'))
.done(function() { console.log('yay!'); });
Angular:
angular.service('update', function($q, $timeout) {
return function(ref, data) {
var def = $q.defer();
ref.update(data, function(err) {
if( err ) def.reject(err);
else def.resolve();
});
return def.promise;
}
});
angular.controller('ctrl', function(update) {
var fb = new Firebase(URL);
update(fb.child('path1'), 'foo')
.then(update.bind(null, fb.child('path2'), 'bar'))
.then(update.bind(null, fb.child('path3'), 'foobar'))
.done(function() { console.log('yay!'); });
});
Bind polyfill (rather handy with Futures): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
You could also pull this off without using .bind if you like this syntax better:
function updateHandler(ref, data) {
return function() {
$.Deferred(function(def) {
ref.update(data, function(err) {
if( err ) def.reject(err);
else def.resolve();
});
});
}
}
var fb = new Firebase(URL);
updateHandler(fb.child('path1'), 'foo')()
.then(updateHandler(fb.child('path2'), 'bar'))
.then(updateHandler(fb.child('path3'), 'foobar'))
.done(function() { console.log('yay!'); });

Related

How to call an asynchronous service N times

Promise in ForEach
I'm having a problem, I need to call a service N times and I've tried this:
This is my function that calls the service, I send a parameter that is "code" and returns a promise.
var get222 = function(codigo) {
var defer = $q.defer();
var cbOk = function(response) {
//console.log(response);
defer.resolve(response);
}
var cbError = function(error) {
//console.log(error);
defer.reject(error);
}
VentafijaAccessService.getProductOfferingPrice(codigo, cbOk, cbError);
return defer.promise;
}
After this function, I get the codes and I need to make a call N times and when they finish returning the promise to get the answer for each code I send.
var getProductOfferingPrice = function(_aCodigoOfertas) {
var deferred = $q.defer();
var results = [];
var promises = [];
angular.forEach(_aCodigoOfertas, function(codigo) {
promises.push(get222(codigo));
});
$q.all(promises)
.then(function(results) {
// here you should have all your Individual Object list in `results`
deferred.resolve({
objects: results
});
});
return deferred.promise;
};
The calls to the services IF THEY ARE EXECUTED, but never returns the promise, I can not get the response of each one.
EDIT
VentaDataService.js
var get222 = function(codigo) {
return $q(function(resolve, reject) {
VentafijaAccessService.getProductOfferingPrice(codigo, resolve, reject);
});
}
var getProductOfferingPrice = function(_aCodigoOfertas) {
return $q.all(_aCodigoOfertas.map(function(codigo) {
return get222(codigo);
}));
};
VentaFijaController.js
var cbOk2 = function(response) {
console.log(response);
}
var cbError2 = function(error) {
console.log(error);
}
VentafijaDataService.getProductOfferingPrice(codigoOfertas)
.then(cbOk2, cbError2)
There's no need to wrap a new promise around this. Just return the $q.all() promise:
VentafijaAccessService.getProductOfferingPriceAllPromise = function(_aCodigoOfertas) {
var promises = [];
angular.forEach(_aCodigoOfertas, function(codigo) {
promises.push(get222(codigo));
});
return $q.all(promises);
};
The resolved value of the returned promise will be an array of results.
VentafijaAccessService.getProductOfferingPriceAllPromise(...).then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
If _aCodigoOfertas is an array, you can further simply getProductOfferingPrice to this:
VentafijaAccessService.getProductOfferingPriceAllPromise = function(_aCodigoOfertas) {
return $q.all(_aCodigoOfertas.map(function(codigo) {
return get222(codigo);
}));
};
You can also vastly simplify get222() to this:
var get222 = function(codigo) {
return $q(function(resolve, reject)) {
// call original (non-promise) implementation
VentafijaAccessService.getProductOfferingPrice(codigo, resolve, reject);
});
}
Then, in the controller, you could do this:
VentafijaDataService.getProductOfferingPriceAllPromise(codigoOfertas).then(function(result) {
console.log(result);
}).catch(function(e) {
console.log('Error: ', e);
});

How to handle chained promises

I'm a little bit confused how should I handle the promises in my situation.
This is my factory:
return {
getCategory: function(categoryId) {
var ref = firebase.database().ref().child('categories').child(categoryId);
var category = $firebaseObject(ref);
return category.$loaded().then(function() {
return category;
});
},
getEntry: function(categoryId, entryId) {
var ref = firebase.database().ref().child('entries').child(categoryId).child(entryId);
var entry = $firebaseObject(ref);
return entry.$loaded().then(function() {
return entry;
});
}
}
In my factory I try to avoid doing like this:
var d = $q.defer();
if() {
d.resolve();
}
return d.promise;
Because the $loaded() returns a promise itself.
And this is my controller:
var categoryId = 'abc';
var entryId = 'def';
// so here i'm getting the category
MyFactory.getCategory(categoryId)
.then(function(category) {
if(category.$value === null)
{
$scope.error = 'The category does not exist';
}
else if(new Date() > new Date(category.expireDate))
{
$scope.error = 'The category has expired';
}
else {
$scope.category = category;
// if no errors
return MyFactory.getEntry(category.$id, entryId);
}
})
.then(function(entry) {
if(entry.$value === null)
{
$scope.error = 'No such entry';
}
else {
$scope.entry = entry;
}
})
.catch(function(error) {
console.error(error);
});
What I want to achieve is to get the category first, and then whether there are some errors or not, get the entry respectively. The data is coming from a Firebase database.
This is kind of working, however I'm not really sure how should I handle the promise when I want to do a next .then and don't nest them one in the other like this:
MyFactory.getCategory().then(function(category) {
if(no category errors) {
MyFactory.getEntry().then(function() {
// ...
});
}
});
For now I'm getting an error in the console (it's type error entry undefined) when for example the category expired or does not exist.
I think I did something wrong in the controller when I return but I'm not really sure and hope you can help me dispel all doubts.
So the real question is how should I handle this correctly, to work as expected?
Thanks.
You should return a rejected promise when there is an error.
Look at the following example:
MyFactory
.getCategory(categoryId)
.then(function (category) {
if (category.$value === null) {
return $q.reject('The category does not exist');
}
if (new Date() > new Date(category.expireDate)) {
return $q.reject('The category has expired');
}
$scope.category = category;
return MyFactory.getEntry(category.$id, entryId);
})
.then(function (entry) {
if (entry.$value === null) {
return $q.reject('No such entry');
}
$scope.entry = entry;
})
.catch(function (error) {
$scope.error = error;
});
Do not forget to inject $q to your controller.
Edit
I would also suggest you move the "error logic" to your service, so the controller would always received either data in .then(data => { ... }) or an error string in .catch(error => { ... }). This would make your controllers cleaner and if you use those service method in a different controller, you would not have to replicate your logic there as well.
Service
return {
getCategory: function(categoryId) {
var ref = firebase.database().ref().child('categories').child(categoryId);
var category = $firebaseObject(ref);
return category.$loaded().then(function() {
if (category.$value === null) {
return $q.reject('The category does not exist');
}
if (new Date() > new Date(category.expireDate)) {
return $q.reject('The category has expired');
}
return category;
});
},
getEntry: function(categoryId, entryId) {
var ref = firebase.database().ref().child('entries').child(categoryId).child(entryId);
var entry = $firebaseObject(ref);
return entry.$loaded().then(function() {
if (entry.$value === null) {
return $q.reject('No such entry');
}
return entry;
});
}
}
Controller
MyFactory
.getCategory(categoryId)
.then(function (category) {
$scope.category = category;
return MyFactory.getEntry(category.$id, entryId);
})
.then(function (entry) {
$scope.entry = entry;
})
.catch(function (error) {
$scope.error = error;
});

Asynchronous handling of services who provide data and retrieve it via http when necessary

I have an angularJS app that utilizes two services to retrieve data from a DB.
session.js
angular.module('RiskAssessment').service('session', ['dbInterface', function(dbInterface) {
this.getBatches = function () {
if (!this.batches) {
console.log("Retrieved Batches");
var that = this;
return this.pullBatches().then(function (data) {
that.batches = data; //Is this EVEN possible?
});
} else {
console.log("Didn't retrieve Batches");
}
return this.batches;
};
this.pullBatches = function () {
return dbInterface.pullBatches(this.getUserId());
};}]);
dbInterface.js
pullBatches: function(userId){
return $http.post('db_queries/get_batches.php', userId)
.then(function (response) {
console.log("get_batches.php POST Result: ", response.data);
return response.data;
})
.catch(function (response) {
console.log("Error post");
});
}
I want to able to get this.batches via getBatches() if it has already been retrieved and set. Otherwise, I'd like to use pullBatches() to retrieve and set this.batches. The answer is probably some mix of promises, but I am struggling with this.
Thank you for reading!
EDIT ::
How do I set this.batches within a .then() of my call to .pullBatches()?
this.getBatches = function(){
if(!this.batches) {
console.log("Retrieved Batches");
var deferred = $q.defer();
deferred = this.pullBatches().then(function(data){
//this.batches = data; <---------------------------- HERE
});
return deferred.promise;
}else{
console.log("Didn't retrieve Batches");
}
return this.batches;
};
EDIT 2 ::
With great help from #Jahirul_Islam_Bhuiyan I fixed my issue.
this.getBatches = function(){
var deferred = $q.defer();
if(!this.batches){
console.log("Retrieved Batches");
dbInterface.pullBatches(this.getUserId()).then(function(payload){
deferred.resolve(payload.data);
service.setBatches(payload.data);
});
}else{
console.log("Didn't retrieve Batches");
deferred.resolve(this.batches);
}
return deferred.promise;
};
this.setBatches = function(batches){
this.batches = batches;
};
In Controller...
session.getBatches().then(function(data){
//console.log("getBatches.then() : " + JSON.stringify(data));
$scope.batches = data;
});
I now have a much greater understanding of promises!
Try following
this.getBatches = function(){
var deferred = $q.defer();
if(!this.batches) {
console.log("Retrieved Batches");
this.pullBatches().then(function(data){
deferred.resolve(data);
});
}else{
console.log("Didn't retrieve Batches");
deferred.resolve(this.batches);
}
var promises = [
deferred.promise
];
var promise = $q.all(promises);
return promise;
};
hope this help

I am trying to fetch data in mongodb using node.js, but i can't get result

I have written query to fetch data from mongodb but i can't get result.
My node.js file is like below
node.js
router.get('/manage-product', function(req, res){
console.log('I received get request');
var findProducts = function(db, callback) {
var cursor =db.collection('proInfo').find( ObjectId("56bc959942559b3847249b7e"));
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
console.dir(doc);
} else {
callback();
}
});
};
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
findProducts(db, function() {
db.close();
});
});
return res.json({
findProducts,
});
});
From this node i can't return the findProducts to my controller. In the firebug response section i am getting only like this {}. But i am getting fetched values in command prompt.
controller.js
(function ()
{
'use strict';
angular
.module('app.manage-product')
.controller('ManageProductController', ManageProductController);
/** #ngInject */
ManageProductController.$inject = ['$http', '$scope'];
function ManageProductController($http, $scope)
{
var vm = this;
$http({
url: 'http://localhost:7200/api/manage-product',
method: 'GET',
//data: ''
}).success(function(res) {
console.log('success');
//$scope.productlist = res;
//vm.findProducts=res;
vm.findProducts=res.findProducts;
}, function(error) {
console.log(error);
alert('here');
});
console.log('i ma here');
}
})();
Use the below code and try
router.get('/manage-product', function(req, res){
console.log('I received get request');
var findProducts = function(db, callback) {
var cursor =db.collection('proInfo').find( ObjectId("56bc959942559b3847249b7e")).toArray(function(err, docs){
if(err){
return callback(new Error("Some problem"));
}else{
return callback(null,docs);
}
});
};
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
findProducts(db, function(err,docs) {
db.close();
if(err) return res.json({result:null})
else
return res.json({result: docs });
});
});
});
return res.json({
findProducts,
});
Remove it and update this in your node.js code
if (doc != null) { console.dir(doc); res.send(JSON.stringify(doc)); db.close(); } else { callback(); }
Try below code in findProducts function
if (doc != null) {
console.dir(doc);
return doc;
}
and your final return should be like below
return res.json(findProducts);

Angularjs async callback return undefined under $scope.$apply();

This is my factory code. The callback is async so i put it under $rootScope.safeApply().
Then I call console.log(authService.authUser) in my controller but it still return undefined when user logged in. But it is find if user not login and will show 'not login' in console. Any idea?
myapp.factory('authService', ['$rootScope', function($rootScope) {
var auth = {};
$rootScope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if (phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
auth.firebaseAuthClient = new FirebaseAuthClient(FIREBASEREF, function(error, user) {
$rootScope.safeApply(function() {
if (user) {
auth.authUser = user;
//auth.isLoggedIn = true;
} else if (error) {
auth.authError = error;
} else {
auth.not = 'not login';
//auth.isLoggedIn = false;
}
});
});
auth.login = function() {
this.firebaseAuthClient.login('facebook');
};
auth.logout = function() {
this.firebaseAuthClient.logout();
};
return auth;
}]);
UPDATED
auth.callback = function(error, user) {
if (user) {
deferred.resolve(user);
} else if (error) {
deferred.reject(error);
} else {
//deferred.reject('not login'); // there is no callback value here
}
return deferred.promise;
}
in controller
callback().then(function(response) {
$scope.isLoggedIn = true;
}, function(response) {
$scope.isLoggedIn = false //How can i set false here?
});
UPDATE 2
Now every thing work fine, I'm able to monitoring user login state. But still having a problem. Check the code below
authService.callback().then(function(success){
$rootScope.isLoggedIn = true; //If promise return success set isLoggedIn true
}, function(fail){
**//If user not login set isLoggedIn false;
//I have problem here because i'm not able to deferred.reject below**
$rootScope.isLoggedIn = false;
})
auth.callback = function(error, user) {
$timeout(function() {
if (user) {
deferred.resolve(user);
} else if (error) {
deferred.reject(error);
} else {
//If this line is added,
//.then() will not return anything not even undefined with no error,
//No mater user logged-in or not login.
//If I comment it out, everything will work fine but how can I
//set isLoggedIn = false?
deferred.reject();
}
}, 0);
return deferred.promise;
}
Wrap the outside service's deferred resolve in a $timeout block to let angular know when its resolved. This way when your controller runs then callback, it'll be in a $digest cycle.
See this fiddle as a working proof of concept: http://jsfiddle.net/Zmetser/rkJKt/
// in controller
authService.login().then(success, error);
// service
myapp.factory('authService', ['$q', '$timeout', function( $q, $timeout ) {
var auth = {},
deferred;
firebaseAuthClient = new FirebaseAuthClient(FIREBASEREF, afterAuth);
function afterAuth( error, user ) {
// Let angular know the deferred has been resolved.
$timeout(function () {
if (user) {
deferred.resolve(user);
} else if (error) {
deferred.reject(error);
} else {
deferred.reject(); // there is no callback value here
}
}, 0);
}
auth.login = function() {
deferred = $q.defer();
firebaseAuthClient.login('facebook');
return deferred.promise;
};
auth.logout = function() {
deferred = $q.defer();
firebaseAuthClient.logout();
return deferred.promise;
};
return auth;
}]);

Resources