Is there a way to find the pendingReuests in new angular apps - angularjs

I'm trying to automate an angular app using selenium. Before running the selenium script I would want to wait for the app to completely load. I used the following code to do this, but after the app was updated to a new angular version, I'm not able to get the pending requests using this method.
I tried searching for a solution for a few days, but couldn't find any. Thanks in advance :)
angular.element(document).injector().get('$http').pendingRequests.length.toString();

In the latest Angular, you can access Pending Requests from the built-in HTTP$ directly
Or if you want to wrap it in a service look ref from here/below
angular.module('app', [])
// This service keeps track of pending requests
.service('pendingRequests', function() {
var pending = [];
this.get = function() {
return pending;
};
this.add = function(request) {
pending.push(request);
};
this.remove = function(request) {
pending = _.filter(pending, function(p) {
return p.url !== request;
});
};
this.cancelAll = function() {
angular.forEach(pending, function(p) {
p.canceller.resolve();
});
pending.length = 0;
};
})
// This service wraps $http to make sure pending requests are tracked
.service('httpService', ['$http', '$q', 'pendingRequests', function($http, $q, pendingRequests) {
this.get = function(url) {
var canceller = $q.defer();
pendingRequests.add({
url: url,
canceller: canceller
});
//Request gets cancelled if the timeout-promise is resolved
var requestPromise = $http.get(url, { timeout: canceller.promise });
//Once a request has failed or succeeded, remove it from the pending list
requestPromise.finally(function() {
pendingRequests.remove(url);
});
return requestPromise;
}
}])

Related

Unit Testing angular $httpBackend service

I've got right now a project were we need to have the backend server mocked for the time being and we are using $httpBackend on the application .run feature. I need to unit test this service that contains the $httpBackend as we will be having a vast amount of mocked calls to the server we will be covering. So right now this is what I have. As a preface to my question the current setup works when I call mockDataService.getWorkflowTask from a controller on a simple page.
My Server replacement service:
angular.module('app').run(function ($httpBackend, $resource, FakeBackendService) {
// TODO: add all necessary http intercepts.
$httpBackend.whenGET('JSON file').respond(function (method, url, data) {
var request = new XMLHttpRequest();
request.open('GET', url, false);
request.send(null);
return [request.status, request.response, {}];
});
$httpBackend.whenGET(/.*/).respond(function (method, url, data) {
return [200, FakeBackendService.getWorkflowTasks(), {}];
});
});
Here is the service for FakeBackendService:
(function () {
'use strict';
var injectParams = [];
function service(lodash) {
var vm = this;
var ret = {
getWorkflowTasks: getWorkflowTasks
};
function getWorkflowTasks() {
if (vm.workflowtasks.length < 1) {
vm.workflowtasks = loadWorkflowTasks("Some JSON file");
}
return vm.workflowtasks;
};
function loadWorkflowTasks(file) {
var workflowTasks = [];
var request = new XMLHttpRequest();
request.open("GET", file, false);
request.send(null);
if (request.status == 200) {
workflowTasks = angular.fromJson(request.response);
}
return workflowTasks;
};
function init() {
vm.workflowtasks = [];
}
init();
return ret;
}
service.$inject = injectParams;
angular.module('mock.FakeBackendService', []).service('FakeBackendService', service);
})();
So that is currently the backend server replacement mock. The following is my data handling service which contains the call to $http.get(blah blah blah).
(function () {
'use strict';
var injectParams = ['$http', '$q', 'mockConfigService', '$httpBackend'];
function factory($http, $q, configService, $httpBackend) {
var vm = this;
var factory = {
getWorkflowTask: getWorkflowTask
};
function getWorkflowTask(str) {
return getResource(str);
}
function init() {
// Get the URL we will be using to get data from
vm.dataServiceURL = configService.getDataServiceURL();
}
function getResource(baseResource) {
var resource = vm.dataServiceURL + baseResource;
return $http.get(resource).then(function (response) {
if (typeof response.data == 'object') {
// Got valid response
return $q.resolve(response.data);
}
else {
// Invalid response
return $q.reject(response.data);
}
}, function (response) {
// Something went wrong
return $q.reject(response.data);
});
}
init();
return factory;
};
factory.$inject = injectParams;
angular.module('mock.dataService', []).factory('mockDataService', factory);
}());
Now for the Jasmine-Karma Unit test.
describe("HTTP Backend Mock testing", function () {
beforeEach(angular.mock.module("app"));
beforeEach(angular.mock.module("mock.FakeBackendService"));
beforeEach(angular.mock.module("mock.configService"));
beforeEach(angular.mock.module("mock.dataService"));
it("Get the workflow task", angular.mock.inject(function (mockDataService) {
var valid = "";
var promise = mockDataService.getWorkflowTask('http://localhost/foo');
promise.then(function (response) {
valid = "Success";
}, function (response) {
valid = "Failure";
});
expect(valid).toBe("Success");
}));
});
Now to the question. So, I'll start by saying I'm new to the AngularJS world and even more so to Jasmine. Anyways, when I debug the unit test I find that the promise's status is still 0 and I always get expected '' to be 'Success' telling my I never resolve (hopefully I'm using the right lingo) the promise from the $http service in mockDataService. I've tried playing around with it some and tried to see if anyone has done this kind of a thing before. I found plenty of examples where the $httpBackend is mocked in the test but none like what I'm attempting. Any ideas or suggestions would be great. Thanks.
EDIT got a slightly working solution
So I decided that I'd by pass the run() service and just do the same response in the expectGET().respond().
describe("HTTP Backend Mock testing", function () {
beforeEach(angular.mock.module("app"));
beforeEach(angular.mock.module("mock.FakeBackendService"));
beforeEach(angular.mock.module("mock.configService"));
beforeEach(angular.mock.module("mock.dataService"));
it("Get the workflow task", angular.mock.inject(function (mockDataService, $httpBackend, FakeBackendService) {
var valid = "";
$httpBackend.expectGET('http://server:80/api/foo').respond(200, FakeBackendService.getWorkflowTasks());
var promise = mockDataService.getWorkflowTask('foo');
promise.then(function (response) {
valid = "Success";
}, function (response) {
valid = "Failure";
});
$httpBackend.flush();
expect(valid).toBe("Success");
}));
});
This sort of solves my testing problem with the run() as the goal was to verify 1) That the regex matching call the correct FakeBackendService and 2) That FakeBackendService returns correct file and actually loads it. I think I can do that by mimicking the same regex in the expectGET. However, I'll leave open for a bit to see if anyone knows how to get the run() to work.
The promise is not going to resolve unless you force it to do so before the test ends. Here is one such way to do it:
$httpBackend.expectGET(......).respond(200, 'abc');
var promise = mockDataService.getWorkflowTask('http://localhost/foo');
promise.then(function (response) {
valid = "Success";
}, function (response) {
valid = "Failure";
});
//new code here
$httpBackend.flush();
expect(valid).toBe("Success");
This will force the promise to resolve and your test should pass. You'll also need to inject the $httpBackend service into the test.
angular.module('mock.dataService', [])
.service('mockDataService', function($http) {
this.getWorkflowTask = function(url) {
return $http.get(url)
}
})
describe('HTTP Backend Mock testing', function() {
var $httpBackend
beforeEach(angular.mock.module("mock.dataService"));
beforeEach(inject(function(_$httpBackend_) {
$httpBackend = _$httpBackend_
}))
it("Get the workflow task", angular.mock.inject(function(mockDataService) {
$httpBackend.expectGET('http://localhost/foo').respond(200);
var promise = mockDataService.getWorkflowTask('http://localhost/foo');
promise.then(function(response) {
valid = "Success";
}, function(response) {
valid = "Failure";
});
$httpBackend.flush();
expect(valid).toBe("Success");
}));
})
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

AngularJS service with promise - reload/refresh

first of all, i'm a noob to AngularJS - hope someone can help with this one. I really don't get it..
I have a AngularJS app, with a "service" that return a promise, that works all well, but the problem is, if my app at the beginning is offline, and afterward gets online, now I want the service promise to refresh.
If my app is online at the beginning, it all works well.
But if it is offline, and later gets online, and the reload button is clicked - the alert("TEST TEST"); never gets executed :(
.service('webService', function ($http, $q, $timeout){
var webservice_url = "http://mywebservice_url";
var deferred = $q.defer();
var service = {};
service.refresh = function() {
$http.get(webservice_url+"?action=get_settings", {timeout: 2000})
.success(function(data) {
deferred.resolve(data);
})
.error(function(err) {
deferred.reject({'error': err});
});
return deferred.promise;
}
this.refresh = function() {
this.promise = service.refresh();
return this.promise;
}
this.promise = service.refresh();
})
.controller('NetworkCtrl', function($scope, $location, $timeout, webService) {
$scope.reloadClick = function(){
webService.refresh().then(function(data) {
alert("TEST TEST");
console.log(data);
});
}
})
I am not 100% positive... cause I can't run your code... but I think that moving line three into the service.refresh method, that should get you closer.

update a service variable within an $http callback

I'm using a service to make user data available to various controllers in my Angular app. I'm stuck trying to figure out how to use the $http service to update a variable local to the service (in my case "this.users"). I've tried with and without promises. The server is responding correctly.
I've read several excellent articles for how to use $http within a service to update the scope of a controller. The best being this one: http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html. That does not help me though because it negates the benefits of using a service. Mainly, modifying the scope in one controller does not modify throughout the rest of the app.
Here is what I have thus far.
app.service('UserService', ['$http', function($http) {
this.users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(this.users);
}
};
promise.then(function() {
// this.users is undefined here
console.log('this.users');
});
}]);
Any help is greatly appreciated. Thank you.
Try using
var users = [];
rather than
this.users = [];
and see what
console.log(users);
outputs in each of those cases.
Your service is oddly defined, but if you have a return in it you can access it from any controller:
app.service('UserService', ['$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(users);
users = data.data;
}
};
return {
getUsers: function(){
return users;
}
}
}]);
so in your controller, you can use:
var myUsers = UserService.getUsers();
UPDATE to use a service correctly here, your service should return a promise and the promise should be accessed in the controller: Here's an example from another answer I gave
// your service should return a promise
app.service('PickerService', [$http', function($http) {
return {
getFiles: function(){
return $http.get('files.json'); // this returns a promise, the promise is not executed here
}
}
}]);
then in your controller do this:
PickerService.getFiles().then(function(returnValues){ // the promise is executed here as the return values are here
$scope.myDirectiveData = returnValues.data;
});
this does not have scope anymore where you are trying to use it do this instead:
app.service('UserService', [$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
console.log(users);
}
};
promise.then(function() {
console.log(users);
});
}]);
all local variables to a service should just be vars if you assign them to this as a property than they will be included every time the service is injected into a controller which is bad practice.
I think what your asking for is a solution along the lines of defining your service like this:
angular.module('app')
.service('User', function($http, $q) {
var users = null;
var deferred = $q.defer()
return {
getUsers: function() {
if(users) {
deferred.resolve(users);
} else {
$http.get('users.json');
.success(function(result) {
deferred.resolve(result);
})
.error(function(error) {
deferred.reject(error);
});
}
return deferred.promise;
}
};
});
Then in one Each controller you would have to do this:
angular.module('app')
.controller('ACtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in BCtrl
$scope.users = users;
});
});
angular.module('app')
.controller('BCtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in ACtrl
$scope.users = users;
});
});
NOTE: Because the deferred.promise the same promise passed to all controllers, executing deferred.resolve(users) in the future will cause all then success callbacks in each of your controllers to be called essentially overwriting the old users list.
All operations on the list will be noticed in all controllers because the users array is a shared object at that point. This will only handle updates to the user list/each individual user on the client side of your application. If you want to persist changes to the server, you're going to have to add other $http methods to your service to handle CRUD operations on a user. This can generally be tricky and I highly advise that you check out ngResource, which takes care of basic RESTful operations

How to cancel $resource requests

I'm trying to figure out how to use the timeout property of a $resource to dynamically cancel pending requests. Ideally, I'd like to just be able to cancel requests with certain attributes (based on the params sent), but it seems this may not be possible. In the meantime, I'm just trying to cancel all pending requests, and then resetting the timeout promise to allow new requests.
The issue seems to be that the $resource configuration only allows a single, static promise for the timeout value. It makes sense how I could do this if I was making individual $http calls, since I could just pass in new promises for the timeout, but how can this work for a $resource? I have set up an example plunker here: http://plnkr.co/edit/PP2tqDYXh1NAOU3yqCwP?p=preview
Here's my controller code:
app.controller('MainCtrl', function($scope, $timeout, $q, $resource) {
$scope.canceller = $q.defer();
$scope.pending = 0;
$scope.actions = [];
var API = $resource(
'index.html', {}, {
get: {
method: 'GET',
timeout: $scope.canceller.promise
}
}
)
$scope.fetchData = function() {
if ($scope.pending) {
$scope.abortPending();
}
$scope.pending = 1;
$scope.actions.push('request');
API.get({}, function() {
$scope.actions.push('completed');
$scope.pending = 0;
}, function() {
$scope.actions.push('aborted');
});
}
$scope.abortPending = function() {
$scope.canceller.resolve();
$scope.canceller = $q.defer();
}
});
Right now, the canceller works when there is a pending request, but I don't seem to be able to reset it - once one request is aborted, all future requests will be aborted as well.
I'm sure I'm missing something, since being able to cancel pending requests seems like a pretty crucial feature of most web applications (at least that I've built).
Thanks
Answer by Gecko IT works for me, but I had to make some modifications in order to:
Enable resource ajax call to be canceled multiple times without need to recreate resource
Make resource backward compatible - This means that there is no need to change any application (Controller) code except resource factory
Make code JSLint compliant
This is complete service factory implementation (you just need to put proper module name):
'use strict';
/**
* ResourceFactory creates cancelable resources.
* Work based on: https://stackoverflow.com/a/25448672/1677187
* which is based on: https://developer.rackspace.com/blog/cancelling-ajax-requests-in-angularjs-applications/
*/
/* global array */
angular.module('module_name').factory('ResourceFactory', ['$q', '$resource',
function($q, $resource) {
function abortablePromiseWrap(promise, deferred, outstanding) {
promise.then(function() {
deferred.resolve.apply(deferred, arguments);
});
promise.catch(function() {
deferred.reject.apply(deferred, arguments);
});
/**
* Remove from the outstanding array
* on abort when deferred is rejected
* and/or promise is resolved/rejected.
*/
deferred.promise.finally(function() {
array.remove(outstanding, deferred);
});
outstanding.push(deferred);
}
function createResource(url, options, actions) {
var resource;
var outstanding = [];
actions = actions || {};
Object.keys(actions).forEach(function(action) {
var canceller = $q.defer();
actions[action].timeout = canceller.promise;
actions[action].Canceller = canceller;
});
resource = $resource(url, options, actions);
Object.keys(actions).forEach(function(action) {
var method = resource[action];
resource[action] = function() {
var deferred = $q.defer(),
promise = method.apply(null, arguments).$promise;
abortablePromiseWrap(promise, deferred, outstanding);
return {
$promise: deferred.promise,
abort: function() {
deferred.reject('Aborted');
},
cancel: function() {
actions[action].Canceller.resolve('Call cancelled');
// Recreate canceler so that request can be executed again
var canceller = $q.defer();
actions[action].timeout = canceller.promise;
actions[action].Canceller = canceller;
}
};
};
});
/**
* Abort all the outstanding requests on
* this $resource. Calls promise.reject() on outstanding [].
*/
resource.abortAll = function() {
for (var i = 0; i < outstanding.length; i++) {
outstanding[i].reject('Aborted all');
}
outstanding = [];
};
return resource;
}
return {
createResource: function (url, options, actions) {
return createResource(url, options, actions);
}
};
}
]);
Usage is the same as in Gecko IT example. Service factory:
'use strict';
angular.module('module_name').factory('YourResourceServiceName', ['ResourceFactory', function(ResourceFactory) {
return ResourceFactory.createResource('some/api/path/:id', { id: '#id' }, {
create: {
method: 'POST'
},
update: {
method: 'PUT'
}
});
}]);
Usage in controller (backward compatible):
var result = YourResourceServiceName.create(data);
result.$promise.then(function success(data, responseHeaders) {
// Successfully obtained data
}, function error(httpResponse) {
if (httpResponse.status === 0 && httpResponse.data === null) {
// Request has been canceled
} else {
// Server error
}
});
result.cancel(); // Cancels XHR request
Alternative approach:
var result = YourResourceServiceName.create(data);
result.$promise.then(function success(data, responseHeaders) {
// Successfully obtained data
}).catch(function (httpResponse) {
if (httpResponse.status === 0 && httpResponse.data === null) {
// Request has been canceled
} else {
// Server error
}
});
result.cancel(); // Cancels XHR request
Further improvements:
I don't like checking if request has been canceled. Better approach would be to attach attribute httpResponse.isCanceled when request is canceled, and similar for aborting.
(for Angular 1.2.28+)Hello All , I just wanted to make that easy to understand , how I handled that issue is as follows :
Here I declare timeout parameter
$scope.stopRequestGetAllQuestions=$q.defer();
then in I use it as follows
return $resource(urlToGet, {}, {get:{ timeout: stopRequestGetAllQuestions.promise }});
and if I want to stop previous $resource calls I just resolve this stopRequestGetAllQuestions object that is all.
stopRequestGetAllQuestions.resolve();
but if I want to stop previous ones and start a new one $resource call then I do this after stopRequestGetAllQuestions.resolve();:
stopRequestGetAllQuestions = $q.defer();
There are quite a lot of examples currently out there.
The following two I've found quite informative:
THis one shows an example how to deal with both $resource and $http requests:
https://developer.rackspace.com/blog/cancelling-ajax-requests-in-angularjs-applications/
and
This one is simpler and is only for $http:
http://odetocode.com/blogs/scott/archive/2014/04/24/canceling-http-requests-in-angularjs.aspx
Hi I made a custom handler based on https://developer.rackspace.com/blog/...
.factory('ResourceFactory', ["$q", "$resource", function($q, $resource) {
function createResource(url, options, actions) {
var actions = actions || {},
resource,
outstanding = [];
Object.keys(actions).forEach(function (action) {
console.log(actions[action]);
var canceller = $q.defer();
actions[action].timeout = canceller.promise;
actions[action].Canceller = canceller;
});
resource = $resource(url, options, actions);
Object.keys(actions).forEach(function (action) {
var method = resource[action];
resource[action] = function () {
var deferred = $q.defer(),
promise = method.apply(null, arguments).$promise;
abortablePromiseWrap(promise, deferred, outstanding);
return {
promise: deferred.promise,
abort: function () {
deferred.reject('Aborted');
},
cancel: function () {
console.log(actions[action]);
actions[action].Canceller.resolve("Call cancelled");
}
};
};
});
/**
* Abort all the outstanding requests on
* this $resource. Calls promise.reject() on outstanding [].
*/
resource.abortAll = function () {
for (var i = 0; i < outstanding.length; i++) {
outstanding[i].reject('Aborted all');
}
outstanding = [];
};
return resource;
}
return {
createResource: function (url, options, actions) {
return createResource(url, options, actions);
}
}
}])
function abortablePromiseWrap(promise, deferred, outstanding) {
promise.then(function () {
deferred.resolve.apply(deferred, arguments);
});
promise.catch(function () {
deferred.reject.apply(deferred, arguments);
});
/**
* Remove from the outstanding array
* on abort when deferred is rejected
* and/or promise is resolved/rejected.
*/
deferred.promise.finally(function () {
array.remove(outstanding, deferred);
});
outstanding.push(deferred);
}
//Usage SERVICE
factory("ServiceFactory", ["apiBasePath", "$resource", "ResourceFactory", function (apiBasePath, $resource, QiteResourceFactory) {
return ResourceFactory.createResource(apiBasePath + "service/:id", { id: '#id' }, null);
}])
//Usage Controller
var result = ServiceFactory.get();
console.log(result);
result.promise.then(function (data) {
$scope.services = data;
}).catch(function (a) {
console.log("catch", a);
})
//Actually cancels xhr request
result.cancel();
One solution is to re-craete the resource every time you need it.
// for canceling an ajax request
$scope.canceler = $q.defer();
// create a resource
// (we have to re-craete it every time because this is the only
// way to renew the promise)
function getAPI(promise) {
return $resource(
'index.html', {}, {
get: {
method: 'GET',
timeout: promise
}
}
);
}
$scope.fetchData = function() {
// abort previous requests if they are still running
$scope.canceler.resolve();
// create a new canceler
$scope.canceler = $q.defer();
// instead of using "API.get" we use "getAPI().get"
getAPI( $scope.canceler.promise ).get({}, function() {
$scope.actions.push('completed');
$scope.pending = 0;
}, function() {
$scope.actions.push('aborted');
});
}
In our attempt to solve this task we got to the following solution:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Cancel resource</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-resource.js"></script>
<script>
angular.module("app", ["ngResource"]).
factory(
"services",
["$resource", function($resource)
{
function resolveAction(resolve)
{
if (this.params)
{
this.timeout = this.params.timeout;
this.params.timeout = null;
}
this.then = null;
resolve(this);
}
return $resource(
"http://md5.jsontest.com/",
{},
{
MD5:
{
method: "GET",
params: { text: null },
then: resolveAction
},
});
}]).
controller(
"Test",
["services", "$q", "$timeout", function(services, $q, $timeout)
{
this.value = "Sample text";
this.requestTimeout = 100;
this.call = function()
{
var self = this;
self.result = services.MD5(
{
text: self.value,
timeout: $q(function(resolve)
{
$timeout(resolve, self.requestTimeout);
})
});
}
}]);
</script>
</head>
<body ng-app="app" ng-controller="Test as test">
<label>Text: <input type="text" ng-model="test.value" /></label><br/>
<label>Timeout: <input type="text" ng-model="test.requestTimeout" /></label><br/>
<input type="button" value="call" ng-click="test.call()"/>
<div ng-bind="test.result.md5"></div>
</body>
</html>
How it works
$resource merges action definition, request params and data to build a config parameter for an $http request.
a config parameter passed into an $http request is treated as a promise like object, so it may contain then function to initialize config.
action's then function may pass timeout promise from params into the config.
Please look at "Cancel Angularjs resource request" for details.

AngularJS : remove $rootScope from a service

I have a service wrapped around WebSocket, I wanted to do it with promises and coupling requests with responses, here is what I came up with:
(function () {
var app = angular.module('mainModule');
app.service('$wsService', ['$q', '$rootScope', '$window', function($q, $rootScope, $window) {
var self = this;
// Keep all pending requests here until they get responses
var callbacks = {};
// Create a unique callback ID to map requests to responses
var currentCallbackId = 0;
var ws = new WebSocket("ws://127.0.0.1:9090");
this.webSocket = ws;
ws.onopen = function(){
$window.console.log("WS SERVICE: connected");
};
ws.onmessage = function(message) {
listener(JSON.parse(message.data));
};
var listener = function (messageObj) {
// If an object exists with callback_id in our callbacks object, resolve it
if(callbacks.hasOwnProperty(messageObj.Request.ID)) {
$rootScope.$apply(
callbacks[messageObj.Request.ID].cb.resolve(messageObj));
delete callbacks[messageObj.Request.ID];
}
};
// This creates a new callback ID for a request
var getCallbackId = function () {
currentCallbackId += 1;
if(currentCallbackId > 10000) {
currentCallbackId = 0;
}
return currentCallbackId;
};
//sends a request
var sendRequest = function (request, callback) {
var defer = $q.defer();
var callbackId = getCallbackId();
callbacks[callbackId] = {
time: new Date(),
cb:defer
};
request.ID = callbackId;
$window.console.log("WS SERVICE: sending " + JSON.stringify(request));
ws.send(JSON.stringify(request));
if(typeof callback === 'function') {
defer.promise.then(function(data) {
callback(null, data);
},
function(error) {
callback(error, null);
});
}
return defer.promise;
};
this.exampleCommand = function(someObject, callback){
var promise = sendRequest(someObject, callback);
return promise;
};
}]);
}());
And I use it in a controller like so:
(function () {
'use strict';
var app = angular.module('mainModule');
app.controller('someController', ['$scope', '$wsService', function ($scope, $wsService) {
$scope.doSomething = function(){
$wsService.exampleCommand(
{/*some data for the request here*/},
function(error, message){
//do something with the response
}
);
};
}]);
}());
After implementing this, I have been told that the service should not really operate on any kind of scope. So my question is - how would I go about removing the $rootScope from the service? I am not even sure if I should get rid of it, and if the conventions say I should, how to approach it. Thanks
I have been told that the service should not really operate on any kind of scope.
Who told you that? It's completely wrong.
Your service is receiving callbacks outside of a digest cycle from the websocket. To work with angular, those updates need to be applied inside a digest cycle - this is exactly what you're doing.
For reference, see the built in $http service. That wraps XMLHttpRequest analogously to how you're wrapping web sockets and it depends on $rootScope for exactly the functionality your code depends on $rootScope for.
Your code demonstrates the canonical use of $rootScope inside a service.

Resources