I have an error in line callback(response.data) telling that callback is not a function. Well, I am new to this and I am trying to figure out how to fix this issue but I am having a tough time. Any idea is appreciated. Thanks
app.service('NotificationPollService', function ($q, $http, $timeout) {
var notification = {};
notification.poller = function (callback, error) {
return $http.get('api/sample.php').then(function (response) {
if (typeof response.data === 'object') {
callback(response.data);
} else {
error(response.data);
}
$timeout(notification.poller, 2000);
});
}
notification.poller();
return notification;
});
You declared poller as a function that receives two functions as its parameters, but you are invoking it with no parameters in two different places:
notification.poller();
and
$timeout(notification.poller, 2000);
Remember you can log the value of the variables to the console as console.log(callback, error), in which case you will see it prints undefined, undefined.
What was the intention of invoking notification.poller() in that line? Looks like that function should be called by the user of your service instead, like this:
notification.poller(data => console.log(`Received some data! ${data}`),
error => console.error(`Some error occurred: ${error}`));
Lastly, what is the intention of poller? By the name I imagined it was going to invoke that endpoint some fixed X number of times (polling) and after X failures it would give up, but it's always invoking poller again, even after a success.
Related
I'm trying to switch to another portal from one portal in the same system and then after switching I need to route in to a specific page. and the href for the page is '#/xx/xx/xx/'.
$root.getData = function (xxx) {
const con = JSON.parse(localStorage.getItem('id'));
if (con !== xxx.id){
var
modalInstance = commonModal.confirmation({
buttons: {
actionText: 'Yes, change it'
},
body: 'xxxxxxxxxxxxxx',
title: 'xxx'
}).result.then(function success(params) {
if (params==='continue') {
return util.changeid(xxx.id).then(function success(params) {
if (params === 'continue') {
($window.location.href = '#/xx/xx/xx/');
}
})
}else {
$window.location.href = '#/xx/xx/xx/';
}
})
} else {
$window.location.href = '#/xx/xx/xx/';
}
};
Here I'm using the .then operator to execute a function after the success of its' parent function.Do I have to pass a promise in order to use the .then operator since I'm using the return that I get from the parent function to execute the next function?
Promise is used to let the function that is listening to the function that you are sending data to , to let know that he is sending a result no matter what. and then using .then you are executing the logic that you need to execute after getting the result that was promised is returned to you
I found that if you are returning from one function to another then is a must to use .then since the function that you are returning the value is expecting a result from the function that sends the value.
So, by using promise we are telling the function that it will get a result, no matter what.
I'm having trouble getting a mocked method that returns a promise to trigger the then connected to it.
Here is the service being tested. The get method is the important aspect of it.
angular.module('service.overtimeRules').factory('OvertimeRules', OvertimeRules);
function OvertimeRules(_, Message, OvertimeRulesModel) {
const rules = [];
return { get };
function get() {
return OvertimeRulesModel.get().then(
data => {
console.log('This never fires :(');
rules.push(...data);
},
() => Message.add('Error retrieving the overtime rules.', 'danger')
);
}
}
Here is the spec file. The error I get is TypeError: undefined is not an object (evaluating 'OvertimeRulesModel.get'). If I remove the get describe wrapper, the error goes away, but the service method's then block still doesn't fire.
describe('OvertimeRules', () => {
let $q, $rootScope, deferred, Message, OvertimeRules, OvertimeRulesModel;
beforeEach(() => {
module('globals');
module('service.overtimeRules');
module($provide => {
$provide.value('Message', Message);
$provide.value('OvertimeRulesModel', OvertimeRulesModel);
});
inject((_$q_, _$rootScope_, _OvertimeRules_) => {
$q = _$q_;
$rootScope = _$rootScope_;
OvertimeRules = _OvertimeRules_;
deferred = { OvertimeRulesModel: { get: $q.defer(), update: $q.defer } };
Message = jasmine.createSpyObj('Message', ['add']);
OvertimeRulesModel = jasmine.createSpyObj('OvertimeRulesModel', ['get']);
OvertimeRulesModel.get.and.returnValue(deferred.OvertimeRulesModel.get.promise);
});
});
describe('get', () => { // why would this cause an error?
it('gets a list of rules and pushes them into the rules array', () => {
deferred.OvertimeRulesModel.get.resolve(['rules']);
OvertimeRules.get();
$rootScope.$digest(); // $apply doesn't work either
expect(OvertimeRules.rules).toEqual(['rules']);
});
});
});
Possibly important notes:
I'm happy to approach this another way as long as the code is concise and readable. Also, I need to reject promises as well.
OvertimeRulesModel is another factory in this service.overtimeRules module; Message is part of a separate module.
I tried moving OvertimeRulesModel to its own module, but got the same error.
I'm using the
A = jasmine.createSpyObj(...); A.get.and.returnValue(promise);
approach in a controller spec and everything is working fine
These are unit tests, so I'd prefer to avoid injecting the actual OvertimeRulesModel service.
Better answer
Seems like I was losing reference the variables being supplied to the module provider. Solution is just to move the createSpyObj calls above the $provide statement.
...
module('service.overtimeRules');
Message = jasmine.createSpyObj('Message', ['add']);
OvertimeRulesModel = jasmine.createSpyObj('OvertimeRulesModel', ['get']);
module($provide => {
$provide.value('Message', Message);
$provide.value('OvertimeRulesModel', OvertimeRulesModel);
});
...
Going with solution fixed another issue I ran into. Also, I didn't have to change the returnValue syntax.
Old answer:
And apparently, all I needed to do was change returnValue to callFake like so:
OvertimeRulesModel = jasmine.createSpyObj('OvertimeRulesModel', ['get']);
OvertimeRulesModel.get.and.callFake(() => deferred.OvertimeRulesModel.get.promise);
Hi I have small factory (myFactory) in my application:
.factory('myFactory', ['$q', function ($q) {
function myMethod() {
.....
}
return {
myMethod: myMethod
};
}]);
I want to get access to myFactory.myMethod() in protractor test so in onPrepare() I'm using
browser.executeScript(function() {
return angular.element(document).injector().get('myFactory');
}).then(function (myFactory) {
console.log('myFactory: ', myFactory);
myFactory.myMethod();
});
for console.log('myFactory: ', myFactory) I see I get object:
myFactory: {
myMethod: {}
}
Then for myFactory.myMethod(); I see error:
TypeError: object is not a function
Anyone know how I can get access to factory from protractor to be able to execute method?
I use services to access user information in my app via Protractor, I went ahead and played around with this as close to your code as I could, my comment above should be your solution. Here's the longer explanation:
So we have a service Users, with a function called getCurrent() that will retrieve the information of the current user. So first time I tried code similar to yours:
browser.executeScript(function () {
return angular.element(document.body).injector().get('Users');
}).then(function (service) {
console.log(service); // logs object which has getCurrent() inside
service.getCurrent(); // error, getCurrent() is not a function
});
This logged the Users object, and included the function getCurrent(), but I encountered the same error as you when I tried to chain the call service.getCurrent().
What DID work for me, was simply moving the .getCurrent() into the execute script. i.e.
browser.executeScript(function () {
return angular.element(document.body).injector().get('Users').getCurrent();
}).then(function (service) {
console.log(service); // logs John Doe, john.doe#email.com etc.
});
So applying that to your case, the below code should work:
browser.executeScript(function() {
return angular.element(document).injector().get('myFactory').myMethod();
}).then(function (myFactory) {
console.log(myFactory); // this should be your token
});
And just a minor FYI, for what it's worth you also could have written this code by passing in a string to executeScript:
browser.executeScript('return angular.element(document).injector().get("myFactory").myMethod()').then(function (val) {
console.log(val);
});
I am trying to retrofit spinners into my app.
I'm expecting to set a loading=true variable when I start async events, and set it false when the call returns.
Then in my view I can do
<span><i class="fa fa-spinner" if-show="vm.loading"></i><span>
I was hoping to find async calls of the form success, failure, finally.
The first controller I opened up makes a call in a form I don't understand. I don't even know what to call it, so I have no idea how to research and explore it.
$scope.login = function () {
if ($scope.form.$valid) {
authService.login($scope.loginData).then(function (response) {
$location.path("/dashboard");
},
function (err) {
toastr.error(err.error_description);
});
}
};
What I see here is an if statement, followed by a comma, followed by a function.
Uhh... is that some form of try/catch I've not encountered before?
I can't just add a finally on the end...
The reason I'm asking the question here is because I don't even know how to research this.
Ultimately the question I'm trying to answer is: what form of async call can I use so that I have a place to first activate the spinner, and then deactivate it?
Ah. OK. It's a standard promise - just confusingly formatted. I overlooked the .then that's on the same line.
$scope.login = function () {
if ($scope.form.$valid) {
$scope.loading = true;
authService.login($scope.loginData)
.then(function (response) {
$location.path("/dashboard");
},
function (err) {
toastr.error(err.error_description);
})
.finally(function(){
$scope.loading = false;
}
);
}
}
Found it here:
How to always run some code when a promise is fulfilled in Angular.js
I am using some data which is from a RESTful service in multiple pages.
So I am using angular factories for that. So, I required to get the data once from the server, and everytime I am getting the data with that defined service. Just like a global variables. Here is the sample:
var myApp = angular.module('myservices', []);
myApp.factory('myService', function($http) {
$http({method:"GET", url:"/my/url"}).success(function(result){
return result;
});
});
In my controller I am using this service as:
function myFunction($scope, myService) {
$scope.data = myService;
console.log("data.name"+$scope.data.name);
}
Its working fine for me as per my requirements.
But the problem here is, when I reloaded in my webpage the service will gets called again and requests for server. If in between some other function executes which is dependent on the "defined service", It's giving the error like "something" is undefined. So I want to wait in my script till the service is loaded. How can I do that? Is there anyway do that in angularjs?
You should use promises for async operations where you don't know when it will be completed. A promise "represents an operation that hasn't completed yet, but is expected in the future." (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)
An example implementation would be like:
myApp.factory('myService', function($http) {
var getData = function() {
// Angular $http() and then() both return promises themselves
return $http({method:"GET", url:"/my/url"}).then(function(result){
// What we return here is the data that will be accessible
// to us after the promise resolves
return result.data;
});
};
return { getData: getData };
});
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
// this is only run after getData() resolves
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
}
Edit: Regarding Sujoys comment that
What do I need to do so that myFuction() call won't return till .then() function finishes execution.
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
console.log("This will get printed before data.name inside then. And I don't want that.");
}
Well, let's suppose the call to getData() took 10 seconds to complete. If the function didn't return anything in that time, it would effectively become normal synchronous code and would hang the browser until it completed.
With the promise returning instantly though, the browser is free to continue on with other code in the meantime. Once the promise resolves/fails, the then() call is triggered. So it makes much more sense this way, even if it might make the flow of your code a bit more complex (complexity is a common problem of async/parallel programming in general after all!)
for people new to this you can also use a callback for example:
In your service:
.factory('DataHandler',function ($http){
var GetRandomArtists = function(data, callback){
$http.post(URL, data).success(function (response) {
callback(response);
});
}
})
In your controller:
DataHandler.GetRandomArtists(3, function(response){
$scope.data.random_artists = response;
});
I was having the same problem and none if these worked for me. Here is what did work though...
app.factory('myService', function($http) {
var data = function (value) {
return $http.get(value);
}
return { data: data }
});
and then the function that uses it is...
vm.search = function(value) {
var recieved_data = myService.data(value);
recieved_data.then(
function(fulfillment){
vm.tags = fulfillment.data;
}, function(){
console.log("Server did not send tag data.");
});
};
The service isn't that necessary but I think its a good practise for extensibility. Most of what you will need for one will for any other, especially when using APIs. Anyway I hope this was helpful.
FYI, this is using Angularfire so it may vary a bit for a different service or other use but should solve the same isse $http has. I had this same issue only solution that fit for me the best was to combine all services/factories into a single promise on the scope. On each route/view that needed these services/etc to be loaded I put any functions that require loaded data inside the controller function i.e. myfunct() and the main app.js on run after auth i put
myservice.$loaded().then(function() {$rootScope.myservice = myservice;});
and in the view I just did
ng-if="myservice" ng-init="somevar=myfunct()"
in the first/parent view element/wrapper so the controller can run everything inside
myfunct()
without worrying about async promises/order/queue issues. I hope that helps someone with the same issues I had.