I am really new to angularJS. I need to develop a page where angular JS wait for a event to happen at server side so angular JS should keep checking server using $http call in every 2 seconds. Once that event completes Angular should not invoke any $http call to server again.
I tried different method but it gives me error like "Watchers fired in the last 5 iterations: []"
Please let me know how to do it.
Following is my code
HTML
<div ng-controller="myController">
<div id="divOnTop" ng-show="!isEventDone()">
<div class="render"></div>
</div>
</div>
Angular JS
var ngApp = angular.module("ngApp",[]);
ngApp.controller('myController', function ($scope, $http) {
$scope.ready = false;
$scope.isEventDone = function () {
$scope.ready = $scope.getData();
return $scope.ready;
};
$scope.getData = function () {
if (! $scope.ready) {
$http.get("/EventManager/IsEventDone")
.then(function (response) {
$scope.ready = Boolean(response.data);
});
}
};
setInterval($scope.isPageReady, 5000);
});
A few things here.
I'm not convinced the accepted answer actually works nor solves the initial problem. So, I'll share my 2 cents here.
$scope.ready = $scope.getData(); will set $scope.ready to undefined each time since this method doesn't return anything. Thus, ng-show="!isEventDone()" will always show the DOM.
You should use angular's $interval instead of setInterval for short-polling in angular.
Also, I've refactored some redundancy.
var ngApp = angular.module("ngApp",[]);
ngApp.controller('myController', function ($scope, $http, $interval) {
var intervalPromise = $interval($scope.getData, 5000);
$scope.getData = function () {
if (! $scope.isEventDone) {
$http
.get("/EventManager/IsEventDone")
.then(function (response) {
$scope.isEventDone = Boolean(response.data);
if($scope.isEventDone) {
$interval.cancel(intervalPromise);
}
});
}
else {
$interval.cancel(intervalPromise);
}
};
});
This should work and solve your initial problem. However, there's a scenario where your server may be on a high load and takes 3 seconds to respond. In this case, you're calling the server every 2 seconds because you're waiting for 5 seconds after the previous request has started and not waiting for after the previous request has ended.
A better solution than this is to use a module like async which easily handles asynchronous methods. Combining with $timeout:
var ngApp = angular.module("ngApp",[]);
ngApp.controller('myController', function ($scope, $http, $timeout) {
var getData = function(cb){
if(!$scope.isEventDone) return cb();
$http.get("/EventManager/IsEventDone")
.then(function (response) {
$scope.isEventDone = Boolean(response.data);
cb();
});
};
// do during will run getData at least once
async.doDuring(getData, function test(err, cb) {
// asynchronous test method to see if loop should still occur
// call callback 5 seconds after getData has responded
// instead of counting 5 seconds after getData initiated the request
$timeout(function(){
cb(null, !$scope.isEventDone);
// if second param is true, call `getData()` again otherwise, end the loop
}, 5000);
}, function(err) {
console.log(err);
// if you're here, either error has occurred or
// the loop has ended with `$scope.isEventDone = true`
});
});
This will call the timeout after the request has ended.
A better alternative, if you have control of the server, is to use a websocket which will enable long-polling (server notifies the client instead of client making frequent requests) and this will not increase significant load on the server as clients grow.
I hope this helps
In your example $scope.pageIsReady does not exist. What you could do is inject the $timeout service into your controller and wrap your http call inside of it:
var timeoutInstance = $timeout(function(){
$http.get("/EventManager/IsEventDone")
.then(function (response) {
$scope.ready = Boolean(response.data);
if($scope.ready){
$timeout.cancel(timeoutInstance);
else
$scope.getData();
}
});
},5000);
cancel will stop the timeout from being called. I have not tested this but it should be along those lines.
Also not sure what type of backend you are using but if it is .net you could look into SignalR which uses sockets so the server side tells the front end when it is ready and therefore you no longer need to use polling.
Related
While I am loading an AngularJS file my code is getting executed completely before the code in .then function completes its execution. How to pause code till the code in .then function executes. I mean I want to make synchronous ajax calls, I used to use async: false in jQuery. I want to know how to do that in angularJS.
Thanks in advance
Below is my AngularJS code
var app = angular.module('myApp', [ ]);
app.controller("Ctrl",Ctrl);
function Ctrl($http){
var self=this
ajaxCall();
function ajaxCall(){
return $http.get('/getData/')
.then(function(data){
// this below alert should come before the last alert
alert(" should execute first then below alert")
self.data=data
})
}
alert("getting executed first")
}
Your ajaxCall() is returning a promise. So you can simply wait until it finished.
The $http API is based on the deferred/promise APIs exposed by the $q service. While for simple usage patterns this doesn't matter much, for advanced usage it is important to familiarize yourself with these APIs and the guarantees they provide.
AngularJS $http documentation
var app = angular.module('myApp', [ ]);
app.controller("Ctrl",Ctrl);
function Ctrl($http){
var self = this;
function ajaxCall(){
return $http.get('/getData/')
.then(function(data) {
// this below alert should come before the last alert
alert(" should execute first then below alert")
self.data = data;
}
);
}
// You can use .then here, because ajaxCall returns a promise
ajaxCall().then(function () {
alert("getting executed first");
})
}
What if you chain promises?
function Ctrl($http, $q){
var self = this;
ajaxCall();
function ajaxCall(){
return $http.get('/getData/')
.then(storeData)
.then(proceed);
}
function storeData(response) {
alert("1st");
self.data = response.data;
return $q.when(self.data);
}
function proceed(data) {
alert("2nd");
}
}
I have still problems with my database, but I found out, that the problems come from the fact, that opening the database takes some time, but the app is not waiting till this task has finished.
Is there a way to make angular wait till the database is opened correctly before it starts the next task?
Thanks,
Christian.
Update: 2016-08-08 - 03:13 pm
Thanks for all the answers. As I can see, my first idea with promises ($q) was right, but:
My app has an app.js, which is my main file. It only calls the InitDB. This should open the database. Then it should call the CreateTables, this creates the table, if it doensn't exist.
The rest of my app is splitted in 4 pages (templates). Every page has it's own controller.
So the idea was to init the db, to create the table, and then work with the database, but used over different controllers.
This won't work, because I would always need to put all of my stuff in the .then() of my initDB in the app.js???
This is my first AngularJS app, maybe this is the reason why I do a lot of mistakes...
Thanks,
Christian.
One of the core concepts of Angular is working with services/factories. There is ample documentation and blogs about how these work and how to use them, but the basic idea is that these are singleton "controllers" that handle shared data and methods across your entire application. Used in combination with promises, you can easily create a service that will manage communications with your database.
angular
.module('myApp')
.service('DBService', DBService)
.controller('Ctrl1', Ctrl1)
.controller('Ctrl2', Ctrl2)
.controller('Ctrl3', Ctrl3)
.controller('Ctrl4', Ctrl4);
DBService.$inject = ['$q'];
function DBService($q) {
var DBService = this;
var DBServiceDeferred = $q.defer();
DBService.ready = DBServiceDeferred.promise;
// a service is only initialized once, so this will only ever be run once
(function() {
init();
})();
function init() {
// you can use promise chaining to control order of events
// the chain will halt if any function is rejected
initDB()
.then(createTablesUnlessExist)
.then(setDbReady);
}
function initDB() {
var deferred = $q.defer();
// simulate async db initialization
$timeout(function() {
deferred.resolve();
// or reject if there is an error
// deferred.reject();
}, 5000);
return deferred.promise;
};
function createTablesUnlessExist() {
//create tables if needed (only happens once)
var deferred = $q.defer();
// simulate async table creation
$timeout(function() {
deferred.resolve();
// or reject if there is an error
// deferred.reject();
}, 5000);
return deferred.promise;
}
function setDbReady() {
DBServiceDeferred.resolve();
}
}
Now you have your DB setup and you don't have to worry about it anymore. You can access the DB from any controller using the service. None of the queries will run until the DB has been initialized and the tables have been created.
Ctrl1.$inject = ['DBService', '$q'];
function Ctrl1(DBService, $q) {
$q.when(DBService.ready).then(function() {
DBService.conn.query("Select something");
});
}
Ctrl2.$inject = ['DBService', '$q'];
function Ctrl2(DBService, $q) {
$q.when(DBService.ready).then(function() {
DBService.conn.query("Select something");
});
}
Ctrl3.$inject = ['DBService', '$q'];
function Ctrl3(DBService, $q) {
$q.when(DBService.ready).then(function() {
DBService.conn.query("Select something");
});
}
Ctrl4.$inject = ['DBService', '$q'];
function Ctrl4(DBService, $q) {
$q.when(DBService.ready).then(function() {
DBService.conn.query("Select something");
});
}
Angular provides a service $q. A service that helps you run functions asynchronously, and use their return values (or exceptions) when they are done processing. Please refer the documentation https://docs.angularjs.org/api/ng/service/$q for the same.
$q basically revolves around the concepts of promises. Promises in AngularJS are provided by the built-in $q service.
Write a method that check if connection is established or not...which returns true or false.
app.controller('MainCtrl', function($scope, httpq) {
http.get('server call method')
.then(function(data) {
if(data.conn==true)
// do what u want
//write other calls
})
.catch(function(data, status) {
console.error('error', response.status, response.data);
})
});
you can use $q library
example:
app.service("githubService", function ($http, $q) {
var deferred = $q.defer();
this.getAccount = function () {
return $http.get('https://api.github.com/users/haroldrv')
.then(function (response) {
// promise is fulfilled
deferred.resolve(response.data);
// promise is returned
return deferred.promise;
}, function (response) {
// the following line rejects the promise
deferred.reject(response);
// promise is returned
return deferred.promise;
})
;
};
});
using above service:
app.controller("promiseController", function ($scope, $q, githubService) {
githubService.getAccount()
.then(
function (result) {
// promise was fullfilled (regardless of outcome)
// checks for information will be peformed here
$scope.account = result;
},
function (error) {
// handle errors here
console.log(error.statusText);
}
);
});
I want to have a list in sync, so I'm (right now) polling it every seconds. But I seem to have a problem - it does not work:
app.controller("MainController", function ($scope, $http, $timeout) {
$scope.responsePromise = $http.get("http://localhost:52219/API/GetList");
$scope.responsePromise.success(function (data, status, headers, config) {
$scope.model.list = JSON.parse(data);
$timeout(function ()
{
console.log("reload");
$scope.responsePromise = $http.get("http://localhost:52219/API/GetList");
}, 1000);
});
My goal is retrieving a list every X sec from the server that talks to the database. Anyone know why does does not spam "reload"? I only get it once
You are looking for $interval, and not $timeout.
from $interval docs:
Angular's wrapper for window.setInterval. The fn function is executed every delay milliseconds.
and also:
Note: Intervals created by this service must be explicitly destroyed when you are finished with them. In particular they are not automatically destroyed when a controller's scope or a directive's element are destroyed. You should take this into consideration and make sure to always cancel the interval at the appropriate moment.
As said by #Nitsan Baleli, you should use the service "$interval" and not "$timeout".
The question was asked here, please see the answer : Angular JS $timeout vs $interval
My goal is retrieving a list every X sec from the server that talks to the database.
I rewrote your code so that it matches your goal, it becomes:
app.controller("MainController", function ($scope, $http, $timeout) {
var $scope.model = {
list:[]
};
var getData = function(){
$http.get("http://localhost:52219/API/GetList").success(function(data){
$scope.model.list = JSON.parse(data);
});
};
getData(); // for the first call
$interval(function (){
getData();
}, 1000);
});
See the plunkr demo here: http://plnkr.co/edit/xCbGGyKPTeJtg7TeKKyE?p=preview
I am new to AngularJS. I am testing resolve in order to display a partial view only after receiving a response from the server (is it the good way to do that?).
To undestand better how resolve works, I would like to add a pause (5 seconds) in the service which uses $resource to get the data displayed by the partial view. In Java I would use the method sleep. Is there a simple way to wait for 5 seconds with AngularJS before executing the following lines of code (before using $resource in my service)?
Something like this will wait:
function ExampleController($scope, $timeout, $q) {
function get() {
var request = $q.defer();
console.log('Waiting...');
$timeout(function() {
request.resolve({data:1});
}, 5000);
return request.promise;
}
get().then(function(response) {
console.log(response.data);
});
}
Basically you set up a $q.defer(), and then resolve it when everything that you want is loaded (or in this case when the timeout finishes). You then use the then function to do something with the data you resolved.
Create a factory like so:
function fakeResource($q, $timeout) {
return {
get: function() {
var request = $q.defer();
$timeout(function() {
request.resolve({data:1});
}, 5000);
return request.promise;
}
}
}]);
app.factory('fakeResource', fakeResource);
Then in the resolve of your $routeProvider:
customers: function(fakeResource) {
return fakeResource.get();
}
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.