I have an angular app and I would like to add a background process that runs without any view. All it is doing is regularly call a webservice and do something about it, no UI.
A quick and dirty way is to simply put the code in window.onLoad. But should I be thinking of doing this the angular way? If so, do I put the code in a Service? How would I "start" this service initially?
You answered it yourself, service is the right choice, you can inject it in any controller you have on app, if that's not the case and you have no controller (or directive) then you can do it in angular.run
angular.module('lookMaMyModule').run(function(injectables){
do something fancy on run
})
Here's simple concept for you, it might have small problems, but you will get the idea.
angular.bootstrap2 = function(module, element, callback){
angular.bootstrap(module, element);
callback();
}
remove ng-app tag from html, and bootstrap app
If background process is ng module, then use angular.module('name').run(); else use self bootstraping technique
app.js
angular.module('name', ['deps']);
angular.bootstrap2(['name'], document.body, function(){
var process_1 = new MyServ();
process_1.start();
});
service.js
var MyServ = function(){
this.intervalId = 0;
this.start = function(){
this.intervalId = setInterval(function(){
console.log('executing');
}, 1000);
}
this.stop = function(){
setInterval.cancel(this.intervalId); //don't remember api, sorry :<
}
});
I think what you're looking for is
app.run()
this gets called when the angular app is starting
see https://docs.angularjs.org/api/ng/type/angular.Module
Related
Today i am creating a page with angular, in controller i am write some jquery Code for toggle div.
Now my question is , is it good practice to write DOM level code in controller or create a directive for this, but i am want such type of code again and again.
vm.getDetails = function (id, event) {
$('.more-row').slideUp(300);
$('.open-content').text("+");
if ($(event.currentTarget).hasClass('open-content')) {
$('.open-content').removeClass('open-content');
return;
}
service.getDetails(id, function (err, model, logs) {
if(err) return;
vm.model.items = model;
vm.model.logs = logs;
vm.model.payRunDetailId = id;
$(event.currentTarget).parent().parent().next().find('td').stop().slideToggle();
$(event.currentTarget).addClass('open-content');
$(event.currentTarget).text("-");
});
};
Its definitely really bad practice. The HTML should go to View or as you pointed out to directive. Controller should just manage the control flow of aplication.
Is this what you are looking for?
https://github.com/EricWVGG/AngularSlideables
there is also link to jsfiddle
using Meteor JS, I would like to start a basic jQuery function (wich will resize some elements) everytime a template is loaded.
I tried to call this function inside Meteor.startup() but this doesn't work.
I also tried to launch my function on Router.onBeforeAction() with no more results (im using ironRouter).
Is there any way to do something like :
Template.someTemplate.created = function(){
myUpdateFunction();
};
But instead of loading this on a specific template, i would like to automaticly load this on every templates in my app.
Is there a way to simply achieve this ?
Thanks.
This will log every Template's name after it is created:
Template.prototype.created = function() {
console.log(this.view.name);
}
However your jquery will require the template to be rendered, so this will probably work better:
Template.prototype.rendered = function() {
console.log(this.view.name);
}
I have managed to achieve it by wrapping Blaze._fireCallbacks :
Blaze._fireCallbacks = (function(_fireCallbacks) {
return function(view, which) {
_fireCallbacks(view, which)
if(which === 'rendered') {
//Add your logic here
}
}
})(Blaze._fireCallbacks)
Might not be the most simple solution. See a demo on this pad.
I am reading AngularJS in Action by Lukas Ruebbelke to clear the concept of dirty checking as to how AngularJS works at a molecular level.
The author puts forward,
It is during the digest cycle that all watch expressions for a scope object
are evaluated. When a watch expression detects that a $scope property has
changed, then a listener function is fired.
Ocassionally a property is changed without AngularJS knowing about it. You
can manually kickstart a digest cycle vis $apply.
So, my question is what are those situations in a real web application when I need to kick off this digest cycle manually. And are those situations often seen? Kindly suggest.
This will come up any time an asynchronous callback returns from a non-angular library. e.g.
setTimeout(function() {
$scope.myVar = 1;
//Angular doesn't know when setTimeout finishes
//so you have to manually kick off a digest cycle.
$scope.$apply();
});
Angular has the $timeout service which takes care of starting a digest cycle for you but if you are using some third party library that takes a callback and doesn't have an angular wrapper then you will have to do this.
These situations can happen when using 3rd party libraries which provide some kind of data for example.
Say you use library-X which fires an event when something happened and new data is available, which you would like to render with AngularJS.
In these causes AngularJS does not know that data in the scope changed if you just directly set the variables.
That is why you should only modify scope variables inside the $apply function:
function MyController($scope) {
$scope.load = function() {
$scope.message = 'Loading...';
setTimeout(function() {
$scope.$apply(function () {
$scope.message = 'Finished loading!';
});
}, 2000);
}
}
It is also advised to use $scope.$apply(function () { /* update code */ }) instead of the single $scope.$apply() call, since it will properly catch errors and run the diggest regardless of any errors.
Hi im currenty using $route.reload to refresh the content of my controller Every time I update my Database. the problem is when updating huge list of data, Every Time I update my Database and run $route.reload my browser lose its ability to scroll up or down my browser, it works fine with smaller list of Data.
below is a sample of my code
$scope.Undone = function(id){
$scope.index = $scope.GetID ;
CRUD.put('/UndoJda/'+$scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
$route.reload();
});
}
Your best bet would be some sort of lazy loading/pagination. So in case it's a really large list, like in the tenths of thousands, it might even be a DOM rendering problem. Also, if that isn't the case, you should try using AngularJS's bind once(Available since 1.3), as well as track by which does not create a watcher for each object on the scope, in your template. Assuming you are using ngRepeat, let's say something like this:
...<ul>
<li ng-repeat="item in Items">
<b>{{item.name}}</b>
</li>
</ul>
Change that to something like the following, in case the data does not update often:
...<ul>
<li ng-repeat="item in Items track by $index">
<b>{{::item.name}}</b>
</li>
</ul>
As a side note, try to always have a dot in your model's name. $scope.Something.list, for eaxample. ("If you don't have a dot, you are doing it wrong" - Misko Hevery himself said this.).
When the data is huge, try to use $timeout and reload the page.
This would prevent very fast refreshes and will keep your page responsive.
$scope.Undone = function(id){
$scope.index = $scope.GetID ;
CRUD.put('/UndoJda/'+$scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
$timeout(function() {
$route.reload();
}, 200);
});
}
You can do it by using $interval
$interval(function() {
CRUD.put('/UndoJda/'+$scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
// Update scope variable
});
}, 2000);
and also don't use $route.reload();. because Angularjs supporting SPA (Single Page Application). if you using $route.reload();. Every time page will loading, So it's not good. you need just call the Service code in inside of interval.
First I would recommend removing usage of $route.reload(), your use case doesn't require the view re-instantiating the controller. Instead you should update the $scope variable that holds the collection of entities your presenting in the view. You will also want to consider adding UX features such as a loading indicator to inform the user about the long running task.
Something similar too the code below would achieve what your looking for. I am unaware of what your CRUD js object instance is, but as long as its Angular aware you will not need to use $timeout. Angular aware usually means non third party APIs, but you can use $q to assist in exposing third party ajax results to angular.
// angular module.controller()
function Controller($scope, EntityService) {
$scope.entityCollection = [];
$scope.loadingData = false; // used for loading indicator
// Something will initialize the entity collection
// this is however your already getting the entity collection
function initController() {
$scope.refreshCollection();
}
initController();
$scope.refreshCollection = function() {
$scope.loadingData = true;
EntityService.getEntitites().then(function(resp) {
$scope.entityCollection = resp;
$scope.loadingData = false;
});
}
$scope.Undone = function(id) {
$scope.index = $scope.GetID ;
CRUD.put('/UndoJda/' + $scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
$scope.refreshCollection();
});
}
}
// angular module.factory()
function EntityService($q, $http) {
return {
getEntitites: function() {
var deferred = $q.defer();
$http.post('/some/service/endpoint').then(function(resp) {
deferred.resolve(resp);
});
return deferred.promise;
}
}
}
I'm currently polling the server to check for new data, and then update the model in an AngularJS app accordingly. He're roughly what I'm doing:
setInterval(function () {
$http.get('data.json').then(function (result) {
if (result.data.length > 0) {
// if data, update model here
} else {
// nothing has changed, but AngularJS will still start the digest cycle
}
});
}, 5000);
This works fine, but most of the requests will not result in any new data or data changes, but the $http service doesn't really know/care and will still trigger a digest cycle. I feel this is unnecessary (since the digest cycle is one of the heaviest operations in the app). Is there any way to still be able to use $http but somehow skip the digest if nothing has changed?
One solution would be to not use $http but jQuery instead, and then call $apply to let Angular know that the model has changed:
setInterval(function () {
$.get('data.json', function (dataList) {
if (dataList.length > 0) {
// if data, update model
$scope.value = dataList[0].value + ' ' + new Date();
// notify angular manually that the model has changed.
$rootScope.$apply();
}
});
}, 5000);
While this seems to work, I'm not sure it's a good idea. I would still like to use pure Angular if possible.
Anyone got any suggestions for improvements to the approach above or a more elegant solution entirely?
P.S. The reason I'm using setInterval instead of $timeout is because $timeout would also trigger a digest cycle which would be unnecessary in this case and only add to the "problem".
Solution provided by AngularJS #doc
AngularJS recommends to use a PERF trick that would bundle up a few $http responses in one $digest via $httpProvider. This again, is not fixing the problem, it's just a sedative :)
$httpProvider.useApplyAsync(true)
Saving the $$watchers solution - risky and not scalable
Firstly, the accepted solution is not scalable - there's no way you're going to do that $watchers trick on a 100K lines of JS code project - it's out of the question.
Secondly, even if the project is small, it's quite risky! What happens for instance if another ajax call arrives that actually needs those watchers?
Another (feasible) risky solution
The only alternative to achieve this without modifying AngularJS code would be to set the $rootScope.$$phase to true or '$digest', make the $http call, and set back the $rootScope.$$phase to null.
$rootScope.$$phase = true;
$http({...})
.then(successcb, failurecb)
.finally(function () {
$rootScope.$$phase = null;
});
Risks:
1) other ajax calls might try to do the same thing --> they need to be synchronized via a wrapping ajax service (over $http)
2) user can trigger UI actions in between, actions that will change the $$phase to null and when the ajax call will come back, and still trigger the $digest
The solution popped after scanning AngularJS source code - here's the line that saves the situation: https://github.com/angular/angular.js/blob/e5e0884eaf2a37e4588d917e008e45f5b3ed4479/src/ng/http.js#L1272
The ideal solution
Because this is a problem that everyone is facing with AngularJS, I think it needs to be addressed systematically. The answers above are not fixing the problem, are only trying to avoid it.
So we should create a AngularJS pull request that would allow us to specify via $httpProvider a config that would not trigger a digest for a specific $http request. Hopefully they agree that this needs to be addressed somehow.
Web sockets would seem to be the most elegant solution here. That way you don't need to poll the server. The server can tell your app when data or anything has changed.
You can do it by this trick :
var watchers;
scope.$on('suspend', function () {
watchers = scope.$$watchers;
scope.$$watchers = [];
});
scope.$on('resume', function () {
scope.$$watchers = watchers;
watchers = null;
});
With this you will remove your scope or reinsert it on the $digest cycle.
You have to manage events to do that of course.
Refer to this post :
Remove and restore Scope from digest cycles
Hope it helps !