I have used $timeout to call an angular JS service in every 5 seconds. But it leads to a page or cursor reload in my application. Can anyone assist me to stop the page reload?
var app = angular.module('myApp', ['ngAnimate']);
app.controller('MainCtrl', function($scope, $http, $timeout) {
var loadTime = 1000, //Load the data every second
errorCount = 0, //Counter for the server errors
loadPromise; //Pointer to the promise created by the Angular $timout service
var getData = function() {
//console.log('http://httpbin.org/delay/1?now=' + Date.now());
$http.get('http://httpbin.org/delay/1?now=' + Date.now())
.then(function(res) {
$scope.data = res.data.args;
errorCount = 0;
nextLoad();
})
.catch(function(res) {
$scope.data = 'Server error';
nextLoad(++errorCount * 2 * loadTime);
});
};
var cancelNextLoad = function() {
$timeout.cancel(loadPromise);
};
var nextLoad = function(mill) {
mill = mill || loadTime;
//Always make sure the last timeout is cleared before starting a new one
cancelNextLoad();
loadPromise = $timeout(getData, mill);
};
//Start polling the data from the server
getData();
//Always clear the timeout when the view is destroyed, otherwise it will keep polling and leak memory
$scope.$on('$destroy', function() {
cancelNextLoad();
});
$scope.data = 'Loading...';
});
Check to make sure loadPromise exists before cancelling it:
var cancelNextLoad = function() {
̶$̶t̶i̶m̶e̶o̶u̶t̶.̶c̶a̶n̶c̶e̶l̶(̶l̶o̶a̶d̶P̶r̶o̶m̶i̶s̶e̶)̶;̶
loadPromise && $timeout.cancel(loadPromise);
};
Related
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;
}
}])
I'm very new to angularjs and I want to establish a connection to my server and dynamically show the result to user. so far I've tried:
angular.module('myApp.controllers', []).controller('socketsController', function($scope) {
$scope.socket = {
client: null,
stomp: null
};
$scope.reconnect = function() {
setTimeout($scope.initSockets, 10000);
};
$scope.notify = function(message) {
$scope.result = message.body;
};
$scope.initSockets = function() {
$scope.socket.client = new SockJS('/resources');
$scope.socket.stomp = Stomp.over($scope.socket.client);
$scope.socket.stomp.connect({}, function() {
$scope.socket.stomp.subscribe('/user/topic/messages', $scope.notify);
});
$scope.socket.client.onclose = $scope.reconnect;
};
$scope.initSockets();
});
But when I use {{result}} nothing appears.
UPDATE
The server response is totally right with console.log(message.body).
I guess, the callback is not taking the scope properly. Try call $scope.$apply(); after you attach the message.body to result :
$scope.notify = function(message) {
$scope.result = message.body;
$scope.$apply();
};
$scope.$apply() triggers an angular digest cycle whcih will update all the bindings..
Call it inside a timeout function but inject $timeout first it will call the digest cycle and update the value.
$timeout(function(){
$scope.result = message.body;});
My app had this .run
.run(function ($rootScope, $http) {
$rootScope.server = "http://127.0.0.1:5000/rawDemo";
var call = $rootScope.server + "/frontEnd/GetStructure";
var texts = {};
texts.languages = {};
$http.get(call).then(function (response) {
for (var i = 0; i < response.data.languages.length; i++) {
texts.languages[response.data.languages[i].iso2] = {
'title': response.data.languages[i].title,
'description': response.data.languages[i].description,
'keywords': response.data.languages[i].keywords,
'frontEndTexts': response.data.languages[i].frontEndTexts
};
}
$rootScope.texts = texts;
$rootScope.webshop = response.data;
$rootScope.webshop.language = response.data.culture.language;
$rootScope.webshop.numberFormat = "";
$rootScope.carouselData = response.data.frontEndConfig.customConfiguration.mjCarousel;
console.log('end run');
});
})
.And some of my resolvers perform a call in a service...
angular
.module('app')
.factory('Products',['$http', '$rootScope', function($http, $rootScope){
return {
listByCategories : function (categories){
console.log('begin service');
var call = $rootScope.server + '/products/list/categories/' + categories.fullPath +'?page1&recordsPerPage=2' ;
return $http.get(call).then(function(response) {
return response.data;
});
}
}
}])
My expected result in console.log supposed to be:
- end run
- begin service
but instead..begin service starts before the end run. It's because the .run finish and keep the $http executing asynchronously and go on to the next stages like resolvers, for example.
According to my research on this website, it's impossible to make the $http works synchronous. So, my question is...any hint about how to handle the scenario ? My service depends from data that's must be loaded before anything.
I cannot "merge" the .run and the service because all the others views, services, must have the .run executed before. in my .run I load dozen of global configurations basically.
UPDATE : I'm still stucked...but I'm trying something...I changed the way I'm doing the things..
so, on my .run
$rootScope.loadWebshop = function(callBack) {
if($rootScope.loaded) { return ; }
var call = $rootScope.server + "/frontEnd/GetStructure";
var texts = {};
texts.languages = {};
$http.get(call).then(function (response) {
for (var i = 0; i < response.data.languages.length; i++) {
texts.languages[response.data.languages[i].iso2] = {
'title': response.data.languages[i].title,
'description': response.data.languages[i].description,
'keywords': response.data.languages[i].keywords,
'frontEndTexts': response.data.languages[i].frontEndTexts
};
}
$rootScope.texts = texts;
$rootScope.webshop = response.data;
$rootScope.webshop.language = response.data.culture.language;
$rootScope.webshop.numberFormat = "";
$rootScope.carouselData = response.data.frontEndConfig.customConfiguration.mjCarousel;
$rootScope.loaded = true;
console.log('end .run');
callBack();
});
}
..And on my service :
listByCategories : function (categories){
return $rootScope.loadWebshop (function () {
console.log('begin service');
var call = $rootScope.server + '/products/list/categories/' + categories.fullPath +'?page1&recordsPerPage=2' ;
return $http.get(call).then(function(response) {
return response.data;
});
});
}
Now I'm facing a different issue...it's returns undefined because it's asyncronous, even using premise. Any clue ??
I'm trying to write an AngularJS library for Pusher (http://pusher.com) and have run into some problems with my understanding of the digest loop and how it works. I am writing what is essentially an Angular wrapper on top of the Pusher javascript library.
The problem I'm facing is that when a Pusher event is triggered and my app is subscribed to it, it receives the message but doesn't update the scope where the subscription was setup.
I have the following code at the moment:
angular.module('pusher-angular', [])
.provider('PusherService', function () {
var apiKey = '';
var initOptions = {};
this.setOptions = function (options) {
initOptions = options || initOptions;
return this;
};
this.setToken = function (token) {
apiKey = token || apiKey;
return this;
};
this.$get = ['$window',
function ($window) {
var pusher = new $window.Pusher(apiKey, initOptions);
return pusher;
}];
})
.factory('Pusher', ['$rootScope', '$q', 'PusherService', 'PusherEventsService',
function ($rootScope, $q, PusherService, PusherEventsService) {
var client = PusherService;
return {
subscribe: function (channelName) {
return client.subscribe(channelName);
}
}
}
]);
.controller('ItemListController', ['$scope', 'Pusher', function($scope, Pusher) {
$scope.items = [];
var channel = Pusher.subscribe('items')
channel.bind('new', function(item) {
console.log(item);
$scope.items.push(item);
})
}]);
and in another file that sets the app up:
angular.module('myApp', [
'pusher-angular'
]).
config(['PusherServiceProvider',
function(PusherServiceProvider) {
PusherServiceProvider
.setToken('API KEY')
.setOptions({});
}
]);
I've removed some of the code to make it more concise.
In the ItemListController the $scope.items variable doesn't update when a message is received from Pusher.
My question is how can I make it such that when a message is received from Pusher that it then triggers a digest such that the scope updates and the changes are reflected in the DOM?
Edit: I know that I can just wrap the subscribe callback in a $scope.$apply(), but I don't want to have to do that for every callback. Is there a way that I can integrate it with the service?
On the controller level:
Angular doesn't know about the channel.bind event, so you have to kick off the cycle yourself.
All you have to do is call $scope.$digest() after the $scope.items gets updated.
.controller('ItemListController', ['$scope', 'Pusher', function($scope, Pusher) {
$scope.items = [];
var channel = Pusher.subscribe('items')
channel.bind('new', function(item) {
console.log(item);
$scope.items.push(item);
$scope.$digest(); // <-- this should be all you need
})
Pusher Decorator Alternative:
.provider('PusherService', function () {
var apiKey = '';
var initOptions = {};
this.setOptions = function (options) {
initOptions = options || initOptions;
return this;
};
this.setToken = function (token) {
apiKey = token || apiKey;
return this;
};
this.$get = ['$window','$rootScope',
function ($window, $rootScope) {
var pusher = new $window.Pusher(apiKey, initOptions),
oldTrigger = pusher.trigger; // <-- save off the old pusher.trigger
pusher.trigger = function decoratedTrigger() {
// here we redefine the pusher.trigger to:
// 1. run the old trigger and save off the result
var result = oldTrigger.apply(pusher, arguments);
// 2. kick off the $digest cycle
$rootScope.$digest();
// 3. return the result from the the original pusher.trigger
return result;
};
return pusher;
}];
I found that I can do something like this and it works:
bind: function (eventName, callback) {
client.bind(eventName, function () {
callback.call(this, arguments[0]);
$rootScope.$apply();
});
},
channelBind: function (channelName, eventName, callback) {
var channel = client.channel(channelName);
channel.bind(eventName, function() {
callback.call(this, arguments[0]);
$rootScope.$apply();
})
},
I'm not really happy with this though, and it feels as though there must be something bigger than I'm missing that would make this better.
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.