I am struggling with the Firebase Queries, I have this Factory:
.factory("usuariosFac", ["$firebaseArray","$q","$firebaseObject",
function($firebaseArray,$q,$firebaseObject) {
return {
getByEmail: function(email){
var ref = firebase.database().ref("usuarios");
var query=ref.orderByChild("email").equalTo(email).on("child_added", function(data) {
console.log(data.val());
return data.val();
});
}
}
}
])
This function is in my Controller:
$scope.findUser = function() {
$scope.usuario=usuariosFac.traeGrupoPorEmail($scope.formLogin.usuario);
};
When I run it, the console Log inside the Factory prints fine. But $scope.usuario is Undefined, why is this?
But $scope.usuario is Undefined, why is this?
The callback function is being called asychronously. The return statement inside a nested function does not return values to the parent function.
Instead, create and return a promise:
app.factory("usuariosFac", ["$firebaseArray","$q","$firebaseObject",
function($firebaseArray,$q,$firebaseObject) {
return {
getByEmail: function(email){
//Create defer object
var future = $q.defer();
var ref = firebase.database().ref("usuarios");
var query=ref.orderByChild("email")
.equalTo(email)
.once("child_added",
function onSuccess(data) {
console.log(data.val());
//RESOLVE
future.resolve(data.val());
},
function onReject(error) {
//OR REJECT
future.reject(error);
}
);
//RETURN promise
return future.promise;
}
}
}
])
In the controller, use the .then method of the returned promise:
$scope.findUser = function() {
var promise = usuariosFac.traeGrupoPorEmail($scope.formLogin.usuario);
promise.then(function onSuccess(data) {
$scope.usuario = data;
}).catch(function onReject(error) {
console.log(error);
throw error;
});
};
Related
I built a service and this service return an object. But, in my controller i don't work with this object because '.then' dont't work.
My service:
var getUser = function(userId) {
Restangular.all(userId).post(JSON.stringify()).then(function(response) {
var obj = angular.fromJson(response);
if (!obj.isError) {
return obj;
}
else {
console.log("ERRO getUserCard");
}
});
};
My controller:
var succsess = function(data){
welcomeScope.getUser = data;
console.log("getUser: " + welcomeScope.getUser);
};
var error = function(){
console.log("Erro error");
};
function loadProfile(){
welcomeSvc.getUser("203831").then(success,error);
};
Note: welcomeScope is my $scope.
you should add return in function getUser
var getUser = function(userId){
return Restangular.all(userId).post(JSON.stringify()).then(function(response){
var obj = angular.fromJson(response);
if (!obj.isError) {
return obj;
}
else{
console.log("ERRO getUserCard");
}
});
};
Your getUser() function needs to return a promise:
var getUser = function(userId) {
return new Promise(function(resolve, reject) {
Restangular.all(userId).post(JSON.stringify()).then(function(response) {
var obj = angular.fromJson(response);
if (!obj.isError) {
resolve(obj);
}
else {
reject(console.log("ERRO getUserCard"));
}
})
})
};
You may need to pass resolve, reject into Restangulars promise
The following function tries to return a promise that will only resolve when all the async HTTP calls have finished:
$scope.saveThat = function () {
var promises = [];
for (var i = 0; i < array.length; i++) {
var newPromise = $q.defer();
promises.push(newPromise);
// some more code...
(function (i, newPromise) {
$http(httpData)
.success(function (response) {
newPromise.resolve('Success');
})
.error(function (response) {
newPromise.reject('Error');
});
})(i, newPromise);
}
return $q.all(promises);
};
And the following snippet calls this function.
// save this...
var promise1 = $rootScope.saveThis(result.Person);
promise1.then(function (success) {
}, function (error) {
saveErrorMessage += 'Error saving this: ' + error + '.';
});
// save that...
var promise2 = $rootScope.saveThat(result.Person);
promise3.then(function (success) {
}, function (error) {
saveErrorMessage += 'Error saving that: ' + error + '.';
});
// wait until all promises resolve
$q.all([promise1, promise2])
.then(
function (success) {
$scope.$emit(alertEvent.alert, { messages: 'Saved successfully!', alertType: alertEvent.type.success, close: true });
}, function (error) {
$scope.$emit(alertEvent.alert, { messages: saveErrorMessage, alertType: alertEvent.type.danger });
});
The problem I have is that the second promise ($q.all([promise1, promise2])) resolves even when the promises in promise2 haven't resolved yet.
Because you are not creating an array of promise, Actually it contains a $q.defer() object. You should be using
promises.push(newPromise.promise);
instead of
promises.push(newPromise);
Also you need to avoid those anti-pattern, because you are creating $q object unnecessarily as you have promise object there which returned from the $http.get.
Code
$scope.saveThat = function() {
var promises = [];
for (var i = 0; i < array.length; i++) {
// some more code...
var promise = $http(httpData)
.then(function(response) {
return 'Success'; //returning data from success resolves that promise with data
}, function(response) {
return 'Error'; //returning data from error reject that promise with data
});
promises.push(promise); //creating promise array
}
return $q.all(promises); //apply $q.all on promise array
};
I'm getting " timeout of 2000ms exceeded. Ensure the done() callback is being called in this test." while unit testing a service call that responds back with a promise. I am expecting a rejected promise.
UNIT TEST - Karma-Mocha-Chai running on PhantomJS
describe('teamService', function () {
var teamSrvc, q;
beforeEach(angular.mock.module('scCommon'));
beforeEach(angular.mock.inject(function ($q, teamService) {
teamSrvc = teamService;
q = $q;
}));
describe('getTeamsByNameStartWith', function () {
it('should return reject promise when passing invalid text to search', function () {
var invalidFirstArg = 132;
var validSecondArg = 10;
return teamSrvc.getTeamsByNameStartWith(invalidFirstArg, validSecondArg).then(
function (result) {
},
function (err) {
err.should.equal("Invalid text to search argument passed.");
}
);
});
});
});
Below is the service i am testing. I tested the teamService while running the site and it does successfully return a rejected promise.
(function (ng) {
'use strict';
ng.module('scCommon')
.service('teamService', ['$q', '$http', function ($q, $http) {
var getTeamsByNameStartWith = function (textToSearch, optLimit) {
var defer = $q.defer();
if (typeof textToSearch != "string") {
defer.reject("Invalid text to search argument passed.");
return defer.promise;
} else if (typeof optLimit != "number") {
defer.reject("Invalid limit option argument passed.");
return defer.promise;
}
$http.get('url')
.success(function (data) {
defer.resolve(data);
})
.error(function () {
defer.reject("There was an error retrieving the teams");
});
return defer.promise;
};
return {
getTeamsByNameStartWith: getTeamsByNameStartWith
}
}])
})(angular);
I've read through other stack overflow answers and non was successful.
Any ideas?
I appreciate the help.
Thanks,
A friend took a look into it and immediately saw the issue. Apparently I needed do a rootScope.$apply.
describe('teamService', function () {
var teamSrvc, q, rootScope;
beforeEach(angular.mock.module('scCommon'));
beforeEach(angular.mock.inject(function ($q, teamService, $rootScope) {
teamSrvc = teamService;
q = $q;
rootScope = $rootScope;
}));
describe('getTeamsByNameStartWith', function () {
it('should return reject promise when passing invalid text to search', function () {
var invalidFirstArg = 132;
var validSecondArg = 10;
teamSrvc.getTeamsByNameStartWith(invalidFirstArg, validSecondArg).then(
function (result) {
},
function (err) {
err.should.equal("Invalid text to search argument passed.");
}
);
rootScope.$apply();
});
it('should return reject promise when passing invalid number to limit', function () {
var validFirstArg = "alex";
var invalidSecondArg = "10";
teamSrvc.getTeamsByNameStartWith(validFirstArg, invalidSecondArg).then(
function (result) {
},
function (err) {
err.should.equal("Invalid limit option argument passed.");
}
);
rootScope.$apply();
});
});
I have written a function which opens an indexedDB. I'm using a promise, because I think it is the cleanest way to handle this.
this.$get = function($q, $rootScope, $window) {
return {
finalize: function() {
var deferred = $q.defer();
var dbRequest = $window.indexedDB.open(dbName, dbVersion);
dbRequest.onupgradeneeded = function(event) {
// XXX do something here.
};
dbRequest.onsuccess = function(event) {
db = event.target.result;
console.log('Success!');
deferred.resolve(event);
};
dbRequest.onerror = deferred.reject;
return deferred.promise;
}
};
};
In order to test this, I have created the following test function:
describe('finalize', function() {
it('should initialize the database', function(done) {
var promise = resource.finalize();
promise.then(function() {
console.log('resolved');
var transaction = resource.db.transaction(['Component']);
expect(transaction).toBeDefined();
done();
});
$rootScope.$apply();
});
});
This prints 'Success!' in the console, but the promise is never resolved.
If I move $rootScope.$apply() to the end of the onsuccess function, the promise is resolved, but only for one test. Other tests then throw an error Error: [$rootScope:inprog] $digest already in progress.
How should I resolve this promise? Is a callback function better?
this.$get = function($q, $rootScope, $window) {
return {
finalize: function() {
var deferred = $q.defer();
var dbRequest = $window.indexedDB.open(dbName, dbVersion);
dbRequest.onupgradeneeded = function(event) {
// XXX do something here.
};
dbRequest.onsuccess = function(event) {
db = event.target.result;
console.log('Success!');
$rootScope.$apply(function () {
deferred.resolve(event);
});
};
dbRequest.onerror = function (error) {
$rootScope.$apply(function () {
deferred.reject(error);
});
}
return deferred.promise;
}
};
};
Also you should use var promise = resource.finalize(); one time for all tests
I have the following factory:
app.factory('clientFactory', function ($http) {
var factory = {};
factory.getClients = function () {
var url = "/tracker/api/client";
$http.get(url).then(function (response) {
return response.data;
});
};
factory.getClient = function (id) {
// TODO
};
factory.insertClient = function (firstName, lastName, city) {
// TODO
};
factory.deleteClient = function (id) {
// TODO
};
return factory;
});
And the controller:
app.controller('ClientController', function ($scope, clientFactory) {
$scope.clients = [];
init();
function init() {
$scope.clients = clientFactory.getClients();
}
$scope.insertCustomer = function () {
// TODO
};
$scope.deleteCustomer = function (id) {
// TODO
};
});
In my controller, 'clients' is always null. I've tried a couple of other approaches like what I see here but I got an error that 'success cannot be called on null' and if I make it past that error, my success function is never called.
What am I missing here?
In your controller, you are treating the getClients method as if it were synchronous. Remember that when you do $http.get, a promise is returned. You need to return that promise to the controller, so it can call .then with a method which will handle a successful result.
Your getClients method needs to look like this:
factory.getClients = function () {
var url = "/tracker/api/client";
return $http.get(url);
};
And I believe your init method needs to look like this:
function init() {
clientFactory.getClients().then(function(response) {
$scope.clients = response.data;
});
}
Try that out!