I have a problem with nested promises, inside an angular service I have this method:
this.get = function (matchId, teamId) {
var _key = matchId + '_' + teamId;
var self = this;
var alivePromise = $apiService.alive();
alivePromise.success(function () {
var getPromise = $apiService.get(matchId, teamId);
getPromise.success(function (response) {
self.clearLocal().then(function () {
return self.pushToLocal({ ots: _key, data: response })
.then(function () {
return self.fetchFromLocal(_key);
});
})
});
getPromise.error(function (response) {
return self.fetchFromLocal(_key);
});
});
alivePromise.error(function () {
return self.fetchFromLocal(_key);
});
};
this.fetchFromLocal = function (key) {
return $indexedDB.openStore('teamsheets', function (store) {
store.find(key);
});
}
this.pushToLocal = function (data) {
return $indexedDB.openStore('teamsheets', function (store) {
store.upsert(data);
});
};
Inside a controller I'd like call this method in this manner:
$dataProvider.get(MATCH_ID, TEAM_ID)
.then(function (result) {
$scope.teamsheet = result;
$scope.masterCopy = JSON.parse(JSON.stringify(result));
});
But I retrieve always the following error:
angular.min.js:107 TypeError: Cannot read property 'then' of undefined
at new <anonymous> (team-sheet-details-controller.js:3)
at Object.e [as invoke] (angular.min.js:39)
at Q.instance (angular.min.js:80)
at L (angular.min.js:61)
at g (angular.min.js:54)
at g (angular.min.js:54)
at angular.min.js:53
at angular.min.js:20
at m.$eval (angular.min.js:132)
at m.$apply (angular.min.js:133)
What I am doing wrong?
if this.get is your get function on $dataProvider then try this code:
this.get = function (matchId, teamId) {
var _key = matchId + '_' + teamId;
var self = this;
return $apiService.alive().then(function () {
return getPromise = $apiService.get(matchId, teamId);
getPromise.success(function (response) {
self.clearLocal().then(function () {
return self.pushToLocal({ ots: _key, data: response })
.then(function () {
return self.fetchFromLocal(_key);
});
})
});
getPromise.error(function (response) {
return self.fetchFromLocal(_key);
});
}, function () {
return self.fetchFromLocal(_key);
});
};
Related
During loading of the partial Html with controller, my function named $scope.actionViewVisitors() is recognized and runs without errors. But whenever I use it inside another function on the same controller, it gives me an error:
TypeError: $scope.actionViewVisitors is not a function. Please see my code below:
angular.module("Visitor.controller", [])
// ============== Controllers
.controller("viewVisitorController", function ($scope, $rootScope, $http, viewVisitorService, viewAccountService, DTOptionsBuilder) {
$scope.visitorList = null;
$scope.viewAccountDetail = null;
$scope.avatar = null;
$scope.visitorDetail = null;
$scope.visitorBtn = "Create";
$scope.actionViewAccount = function () {
$scope.actionViewAccount = viewAccountService.serviceViewAccount()
.then(function (response) {
$scope.viewAccountDetail = response.data.account;
$scope.avatar = "../../avatars/" + response.data.account.AccountId + ".jpg";
})
}
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withDisplayLength(10)
.withOption('bLengthChange', false);
// THIS ONE IS NOT RECOGNIZED
$scope.actionViewVisitors = function () {
$scope.actionViewVisitors = viewVisitorService.serviceViewVisitors()
.then(function (response) {
debugger;
$scope.visitorList = response.data.visitorList;
});
}
// I DON'T GET ANY ERROR HERE
$scope.actionViewVisitors();
$scope.actionViewAccount();
$scope.createVisitor = function () {
$scope.statusMessage = null;
if ($scope.visitorBtn == "Create") {
$scope.createVisitor = viewVisitorService.serviceCreateVisitor($scope.visitorDetail)
.then(function (response) {
if (response.data.response == '1') {
bootbox.alert({
message: "Successfully created a new visitor.",
size: 'small',
classname: 'bb-alternate-modal'
});
} else if (response.data.response == '0') {
bootbox.alert({
message: "Failed in creting visitor.",
size: 'small',
classname: 'bb-alternate-modal'
});
}
});
debugger;
$scope.visitorDetail = undefined;
// I GET THE ERROR WHEN I CALL THIS METHOD
$scope.actionViewVisitors();
}
}
})
// ============== Factories
.factory("viewVisitorService", ["$http", function ($http) {
var fac = {};
fac.serviceViewVisitors = function () {
return $http({
url: '/Visitor/ViewVisitors',
method: 'get'
});
}
fac.serviceCreateVisitor = function(visitor) {
return $http({
url: '/Visitor/CreateVisitor',
data: { visitor: visitor },
method: 'post'
});
}
return fac;
}])
You are overwriting the function with Promise in the following line, thus the error is correct
$scope.actionViewVisitors = function () {
$scope.actionViewVisitors = viewVisitorService.serviceViewVisitors()
.then(function (response) {
$scope.visitorList = response.data.visitorList;
});
}
Remove $scope.actionViewVisitors =
$scope.actionViewVisitors = function () {
viewVisitorService.serviceViewVisitors()
.then(function (response) {
$scope.visitorList = response.data.visitorList;
});
}
On the first call to the function you are changing it from a function to a Promise. Maybe you want to be returning the result instead?
$scope.actionViewVisitors = function () {
return viewVisitorService.serviceViewVisitors()
.then(function (response) {
debugger;
$scope.visitorList = response.data.visitorList;
});
}
Service call in for loop angular js $q, promise
var FULLWEEKDAYS = [MONDAY, TUESDAY ... SATURDAY]
for (var i=0; i< FULLWEEKDAYS.length; i++) {
var reqParams = {
weekday: FULLWEEKDAYS[i],
teacherId : 97
}
TimetableService.getTeachersOccupancy(reqParams, function (data)
{
if (data) {
$scope.weeklyData.push(data);
}
}, function (err) {
//message.error('Timetable', err.data);
});
}
Serivice call is
function getTeachersOccupancy(data, successFunction, errorFunction) {
var params = $.param(data);
AjaxHandlerFactory.AjaxGet(BASETIMETABLEPATH + 'occupancy?' +
params, {}, function (response) {
successFunction(response.data);
}, function (error) {
errorFunction(error);
});
}
Question:
$scope.weeklyData.length = 0 outside for loop. Why and how to handle this in promises?
Serivce call
function getTeachersOccupancy(data, successFunction, errorFunction) {
// /SchoolAdminWS/services/schools/{schoolCd}/timeTable/occupancy?classroomId={classroomId}&date={YYYY-MM-DD}
var params = $.param(data);
***var deferred = $q.defer();***
AjaxHandlerFactory.AjaxGet(BASETIMETABLEPATH + 'occupancy?' + params, {}, function (response) {
successFunction(response.data);
***deferred.resolve(response.data);***
}, function (error) {
errorFunction(error);
***deferred.reject(error);***
});
***return deferred.promise;***
}
While calling above service, create a variable promise=[]; push all repsonses from service call, and resolve them.
var promises = [];
for (var i=0; i< FULLWEEKDAYS.length; i++) {
var reqParams = {
weekday: FULLWEEKDAYS[i],
teacherId : vm.employeeProfileId
}
var promise = TimetableService.getTeachersOccupancy(reqParams, function () {}, function () {});
promises.push(promise);
}
Now resolve using $q.all()
$q.all(promises).then(function(value) {
vm.weeklyData = value;
console.log(vm.weeklyData);
setTeacherOccupancyData(value);
vm.isSearch = true;
}, function (reason) {
console.log("Promise Rejected:" + reason);
});
I'm having a controller and servise (I will post all of controller and service code because I have no idea what could be wrong) :
Controller:
'use strict';
app.controller('membersController', ['$scope', 'membersService', function($scope, membersService) {
$scope.members = [];
$scope.updatedMembers = [];
membersService.getMembers().then(function (results)
{
$scope.members =results.data;
},
function(error) {
alert(error.data.message);
});
$scope.update = function () {
membersService.updateMembers($scope.updatedMembers).then(function(results) {
alert(results);
},
function(results) {
alert(results);
});
};
$scope.updateActive = function(member) {
if ( !isInArray($scope.updatedMembers,member))
{
$scope.updatedMembers.push(member);
}
};
var isInArray = function(array, item) {
var found = false;
for (var i = 0; i < array.length; i++) {
if (array[i].id == item.id) {
found = true;
break;
}
}
return found;
};
}]);
Service:
'use strict';
app.factory('membersService', ['$http', 'ngAuthSettings', function ($http, ngAuthSettings) {
var serviceBase = ngAuthSettings.apiServiceBaseUri;
var membersServiceFactory = {};
var _getMembers = function () {
return $http.get(serviceBase + 'api/members').then(function (results) {
return results;
});
};
var _updateMembers = function(updatedMembers) {
$http.post(serviceBase + 'api/Members/UpdateMembers', updatedMembers, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(
function (results) {
return results;
},
function(results) {
return results;
});
};
membersServiceFactory.getMembers = _getMembers;
membersServiceFactory.updateMembers = _updateMembers;
return membersServiceFactory;
}]);
This is error that i'm getting in firebug:
Error:
membersService.updateMembers(...) is undefined
$scope.update#http://localhost:37272/Controllers/membersController.js:16:13
$a.prototype.functionCall/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:172:370
fc[c]</<.compile/</</<#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:189:395
Yd/this.$get</h.prototype.$eval#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:108:471
Yd/this.$get</h.prototype.$apply#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:109:230
fc[c]</<.compile/</<#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:189:370
ne/c/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:31:30
q#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:7:363
ne/c#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js:31:14
Can anyone at LEAST point me to the right direction as i'm new in angular.js. Also i would like to notice that request is passed to .net webapi even with this error
If in controller you want to use it like a promise object, I mean:
$scope.update = function () {
membersService.updateMembers($scope.updatedMembers).then(function(results) {
alert(results);
},
function(results) {
alert(results);
});
};
then you should return promise object, $http get method itself is returning promise.
'use strict';
app.factory('membersService', ['$http', 'ngAuthSettings', function ($http, ngAuthSettings) {
var serviceBase = ngAuthSettings.apiServiceBaseUri;
var membersServiceFactory = {};
var _getMembers = function () {
return $http.get(serviceBase + 'api/members');
};
var _updateMembers = function(updatedMembers) {
$http.post(serviceBase + 'api/Members/UpdateMembers', updatedMembers, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });;
};
membersServiceFactory.getMembers = _getMembers;
membersServiceFactory.updateMembers = _updateMembers;
return membersServiceFactory;
}]);
So when I try and run my jasmine tests, i get this error on every test:
TypeError: Property '$' of object [object Object] is not a function
at jasmine.Fixtures.cleanUp (http://localhost:8234/src/target/test-classes/frontend/thirdParty/js/com/github/velesin/jasmine-jquery/0.0.0/jasmine-jquery-0.0.0.js:117:3)
at null.<anonymous> (http://localhost:8234/src/target/test-classes/frontend/thirdParty/js/com/github/velesin/jasmine-jquery/0.0.0/jasmine-jquery-0.0.0.js:548:25)
at jasmine.Block.execute (http://localhost:8234/?:1152:19)
at jasmine.Queue.next_ (http://localhost:8234/?:2184:33)
at jasmine.Queue.start (http://localhost:8234/?:2137:10)
at jasmine.Spec.execute (http://localhost:8234/?:2464:16)
at jasmine.Queue.next_ (http://localhost:8234/?:2184:33)
at onComplete (http://localhost:8234/?:2180:20)
at jasmine.Spec.finish (http://localhost:8234/?:2438:7)
at null.onComplete (http://localhost:8234/?:2465:12)
I've seen the various posts and SO's about jquery running in noConflict mode and that I need to use jQuery throughout my code, but the code I'm testing doesnt have any $'s in it.
code:
$provide.factory('corsHttpInterceptor', ['$q', '$window', '$exceptionHandler', '$rootScope', 'jq360', function($q, $window, $exceptionHandler, $rootScope, jq360){
var corsHttpInterceptor,
ieCorsTimeoutTime;
function fixConfigForXdr(config)
{
if (config.method.toUpperCase() === "PUT")
{
config.method = "POST";
if (angular.isDefined(config.params))
{
config.params._method = "put";
}
else
{
config.params = {_method: "put"};
}
}
else if (config.method.toUpperCase() === "DELETE")
{
config.method = "GET";
if (angular.isDefined(config.params))
{
config.params._method = "delete";
}
else
{
config.params = {_method: "delete"};
}
}
}
function getResponseDataForXdr(xdr)
{
var responseData = xdr.responseText;
if ("application/json" === xdr.contentType)
{
responseData = angular.fromJson(responseData);
}
return responseData;
}
function getIEUrl(config)
{
var url = config.url;
if (angular.isDefined(config.params) && !angular.equals(config.params, {}))
{
if (-1 === url.indexOf("?"))
{
url += "?";
}
else
{
url += "&";
}
url += jq360.param(config.params);
}
return url;
}
corsHttpInterceptor = {
request: function(config){
var deferred,
promise,
xdr;
if ('withCredentials' in new $window.XMLHttpRequest())
{
return config;
}
else if (angular.isDefined($window.XDomainRequest))
{
config.method = angular.uppercase(config.method);
deferred = $q.defer();
//this promise already has the then function so don't need to add it
promise = deferred.promise;
try
{
xdr = new $window.XDomainRequest();
}
catch(e)
{
$exceptionHandler(new Error("CRITICAL: " + e.message), "new XDomainRequest()");
}
try
{
fixConfigForXdr(config);
xdr.open(config.method, getIEUrl(config));
}
catch(e)
{
$exceptionHandler(new Error("CRITICAL: " + e.message), "xdr.open");
}
xdr.onprogress = function() {}; //http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/30ef3add-767c-4436-b8a9-f1ca19b4812e/
xdr.ontimeout = function() {};
xdr.onload = function() {
try
{
var responseData = getResponseDataForXdr(xdr);
deferred.resolve({data: responseData, status: 200});
$rootScope.$apply();
}
catch(e)
{
$exceptionHandler(new Error("CRITICAL: " + e.message), "xdr.onload");
}
};
xdr.onerror = function() {
try
{
deferred.reject({data: "", status: 500});
$rootScope.$apply();
}
catch(e)
{
$exceptionHandler(new Error("CRITICAL: " + e.message), "xdr.onerror");
}
};
xdr.timeout = 0;
$window.setTimeout(function() { //IE CORS requests are inconsistent without the setTimeout. Reference: http://stackoverflow.com/questions/5250256/inconsistent-ajax-xdr-response-from-ie
try
{
if ("GET" === config.method)
{
xdr.send();
}
else
{
xdr.send(angular.toJson(config.data));
}
}
catch(e)
{
$exceptionHandler(new Error("CRITICAL: " + e.message), "xdr.send");
}
}, ieCorsTimeoutTime);//TEMPORARY way to dynamically set the timeout time for IE CORS requests
promise.success = function(fn) {
promise.then(function(response) {
fn(response.data, response.status);
});
return promise;
};
promise.error = function(fn) {
promise.then(null, function(response) {
fn(response.data, response.status);
});
return promise;
};
return promise;
}
else
{
throw new Error("Browser doesn't support needed functionality.");
}
},
response: function(response){
return response;
},
responseError: function(rejection){
return $q.reject(rejection);
},
ieCorsTimeoutTime: ieCorsTimeoutTime
};
return corsHttpInterceptor;
}]);
test:
'use strict';
var mockAppbaseModule;
describe('appbaseWithCors', function(){
mockAppbaseModule = angular.module("appbase", []);
beforeEach(module(function($provide, $exceptionHandlerProvider) {
$provide.provider('jq360', function() {
this.$get = function() {
return $;
};
});
$exceptionHandlerProvider.mode('log');
}));
beforeEach(module('appbaseWithCors'));
describe("corsHttpInterceptor", function () {
var successCallback = null;
var errorCallback = null;
var successResponse = {foo: 'blah'};
var errorResponse = {errorCode: 123, appServer: 1};
beforeEach(inject(function() {
successCallback = jasmine.createSpy("successCallback");
errorCallback = jasmine.createSpy("errorCallback");
}));
var appStatus;
describe("Standard CORS", function () {
beforeEach(inject(function($window){
appStatus = {
appBaseUrl : "",
appServer: 1,
token: "TEST_TOKEN"
};
spyOn($window, "XMLHttpRequest").andReturn({withCredentials: 'foo'});
}));
it ("should call the error function when there is an error code in the response data", inject(function($http, $httpBackend) {
$httpBackend.expectGET("TEST_URL").respond(403, errorResponse);
var config = {method: "get", url:"TEST_URL"};
$http(config).success(successCallback).error(errorCallback).then(successCallback, errorCallback);
$httpBackend.flush();
expect(successCallback).not.toHaveBeenCalled();
expect(errorCallback).toHaveBeenCalledWith({data: errorResponse, status: 403, headers: jasmine.any(Function), config: jasmine.any(Object)});
}));
}));
}));
}));
It is possible to have a dynamic file resource?
This is my factory
factory('fileResourcedc', function ($resource) {
var FileResourcedc = $resource(
'xml/file.json',{},
{
get:{method:'GET', isArray:false}
}
);
return FileResourcedc;
})
And I am calling it from here:
var deferred = $q.defer();
var successFn = function (result) {
if (angular.equals(result, [])) {
deferred.reject("Failed because empty : " + result.message);
}
else {
deferred.resolve(result);
}
};
var failFn = function (result) {
deferred.reject("Failed dataconfResponse");
};
fileResourcedc.get(successFn, failFn);
return deferred.promise;
Note that in my factory, the filename is hard coded:
'xml/file.json'
What I need is to create a filename parameter and pass it to factory service. Is it possible?
Thaks in advance
This was my solution:
factory('fileResourcedc', function ($resource) {
var FileResourcedc = $resource(
'xml/:myFile',
{},
{
get:{method:'GET', params:{myFile:""}, isArray:false}
}
);
FileResourcedc.prototype.getCatalogue = function (fileName, successCat, failCat) {
return FileResourcedc.get({myFile:fileName}, successCat, failCat);
};
return new FileResourcedc;
})
Call:
var deferred = $q.defer();
var successFn = function (result) {
if (angular.equals(result, {})) {
deferred.reject("No catalogue");
}
else {
deferred.resolve(result);
}
};
var failFn = function (result) {
deferred.reject("Failed catalogue");
};
fileResourcedc.getCatalogue("catalogues.json",successFn, failFn);
return deferred.promise;
Thanks!