When to use AngularJS services instead of JavaScript native functions/objects? - angularjs

I am new to AngularJS and I am confused when to use them and why they were built if native JavaScript object could have served the purpose. The AngularJS services in question are $location, $window, $document etc.

Most of the services doesn't really do anything. For example $window is just window wrapped in function and provider. But in a few cases you have to use services provided by AngularJS to remain in it's scope so your app can track changes. Any code that runs outside of AngularJS framework isn't tracked by it, so if you would use:
setTimeout(() => {
console.log("This is outside AngularJS scope.");
this.myName = "John";
}, 1000);
HTML binded to 'myName' wouldn't be updated. You'd have to call $digest so AngularJS could refresh changes or wrap your code in $apply(() => { your code }). So that's why AngularJS provides these services. Which are just default functions provided by browser but wrapped in $apply function.

I think the primary purposes of these services are for mocking or testing the application. They are also limited to the current scope so you don't have to worry about accidentally overwriting stuff in the global object.
Another small advantage is that dependencies when uglyfied are replaced while the global object window would stay the same.

Related

In an angular hybrid app, how exactly does downgradeModule work in relation to the Angular zone?

I'm mainly interested in the impact of this line in their documentation:
Unlike UpgradeModule, downgradeModule() does not bootstrap the main AngularJS module inside the Angular zone.
Does this mean that just the initial bootstrap of the application will run outside of the Angular zone? Or, will the services, factories, etc that get instantiated also run outside the Angular zone?
If it's just the initial bootstrap, then how do you run specific pieces of the AngularJS side outside of the Angular zone (mainly for performance reasons)?
A simple example that explains why I'm confused is an AngularJS service that does something like this:
element.on('click', handlerFn);
function handlerFn () {
scope.$apply(function () {
// update some scope binding
});
}
In this example, the click handler is happening outside of the change detection in AngularJS, which is why the scope.$apply is required.
However, the Angular change detection is a different story:
First, there are events that are patched to automatically work inside of the angular zone.
Second, I've noticed that if the service is called from within an UpgradeComponent, it's inside of the Angular zone, but it appears that if it's called from another part of the application, that is not originating from an UpgradeComponent, then it is not in the zone.
Is this only because it's being called by an upgraded component? And if so, is there some standard workaround for having this code run outside the Angular zone, like during the bootstrap or in the UpgradeComponent? Or is it bad practice to even try to do this (like if it would cause syncing issues between Angular and AngularJS)?
I can try to produce a minimal example if necessary.
Edit:
This isn't exactly the environment I'm working with (in terms of angular versions), but here's an example of the behavior I'm talking about with the angular zone.

Configuring shared services across multiple modules in AngularJS

My app is following John Papa's styleguide for AngularJS applications:
The styleguide emphasizes using a strongly modular approach to the design of the app. My question is about multiple configurations and their effect on shared services.
Suppose I have a main module like this:
angular.module("app", ["app.feature1"])
.config(function() {
// do some configuration here
console.log("app configured");
});
And a feature module that configures a shared angular service, let's say the $http service:
angular.module("app.feature1", [])
.config(function($http) {
// configure the $http service
console.log("feature1 configured");
});
Is my understanding correct, that the configuration by "feature1" will carry over to the main module, since the $http service is a singleton and therefore shared across modules? Or do I have to configure the $http service in the main module instead, because each module has it's own $http service instance?
Edit: I can confirm that dependency configs are carried over and are executed first. See David's jsfiddle example.
As a matter of best practice, you should configure services as early as possible, which is typically your main module (the app root), and preferably only once to avoid overlapping changes.
Since $http is a singleton (as you mentioned), any changes via configuration will be propagated throughout the application every time you inject $http.
It's also worth mentioning that configuration to services is First In First Out, meaning that if you have two configuration changes, the last-accessed configuration will be the one that is persisted to the service since the previous configuration will be overwritten if they are changing identical components of the service.
In your case, yes, the change to $http in your module will be extended to your main application and any other modules using $http.
Finally, in light of the comments, child dependency configs are resolved before parent configs, as demonstrated by this simple fiddle:
http://jsfiddle.net/maqzo6fv/
HTML:
<div ng-app="app"></div>
JS:
angular.module("app", ["app.feature1"])
.config(function() {
alert('main config');
});
angular.module("app.feature1", [])
.config(function() {
alert('child config');
});
The child config will call alert before main on every load.

Detecting page unload in angularjs

I have a javascript object which I need global to my angularjs application. When the application is about to be unloaded (due to a page refresh or some other scenario), I would like to persist the object to browser storage. I believe adding the object to $rootScope would fulfill the first requirement of making the object global, but writing the object to storage during the onbeforeunload event, would require access to the $rootScope variable. Is it possible to detect a page unload event in angularjs?
This should do it, but I also suggest avoiding $rootScope as global state is discouraged, and you might also wrap localStorage in a service so that it can be injected and mocked for testing.
.run([
'$window',
'$rootScope',
function($window, $rootScope) {
$window.addEventListener('beforeunload', function() {
localStorage.myValue = $rootScope.myValue;
});
}
]);

How to initialize controller manually after app has bootstrapped?

I trying to add angular widgets into an existing non-angular app. The problem is that these widgets are not added by Angular, and are mostly injected into the DOM by a non-angular script.
The first thing I thought was to use angular.bootstrap for every widget. But the issue with that approach is that I cannot share services between widgets. So, instead of doing that, I want to bootstrap the Angular app once, and load the controllers manually for each widget.
Here is a sandbox jsbin where I've been trying stuff without success
I'm trying to make app1 manually and app2 automatically, while sharing the sharedThing service.
Well, I found my answer.
I don't know if I had solved it before, but I had to make a call to $apply after recompiling the element to see the changes without manually modifying the input. Also, I discovered that I can initialize the angular app with an element outside the DOM and I can compile elements outside the root element! That is perfect for what I'm trying to achieve.
Anyway, here is the jsBin with the code working.
In the app config
app.config(function($controllerProvider) {
app.register =
{
controller: $controllerProvider.register
};
}
then after you declare controller function
app.register.controller('CtrlName',CntrlFn]);
Dan had wrote a great article about it. http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx

Lazy-load external JavaScript in one of AngularJS controller

Some of my routes needs functionality from external JS. I don't want to load them all at once since those JS are needed only in certain routes (e.g. /upload needs some JS for photo uploading, /photos needs another JS for lightbox, /funny needs JS for animation stuff, etc).
What's the best practice for lazily loading those external JavaScripts?
Those routes can be accessed multiple times (e.g. user can go to /upload then /photos then /upload again)
In addition to what Alex has stated, if you will be lazy loading AngularJS artefacts such as controllers and directives, you would have to use the relevant provider to register them instead of the module API. Artefacts registered using the module API after the application has been bootstrapped, will not be available to the application. For example, you would define a lazy controller like this...
$controllerProvider.register('SomeLazyController', function($scope)
{
$scope.key = '...';
});
instead of this...
angular.module('app').controller('SomeLazyController', function($scope)
{
$scope.key = '...';
});
I have written a blog post detailing this as well as how to use the 'resolve' method that Alex speaks about, to implement lazy loading in AngularJS. http://ify.io/lazy-loading-in-angularjs/
The only way I know to handle cases like this is using the "resolve" method of a route. This method can be used to define a dependency to be loaded before the route's controller is instantiated. One of the different possible return types of this method is a promise. Thus you might use this to start loading your external JavaScript code asynchronously and return a promise that is resolved as soon as your external scripts are loaded.
The documentation for this can be found here: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider in the "when" section.
#alex3683's answer is probably the correct AngularJS way, but I don't grasp the concept, so instead I make use of jQuery's getScript(). So, in CoffeeScript:
yourModule.factory 'foo', ->
$.getScript 'https://script-to-load', ->
# whatever you want to do once the script is loaded
And just call it from your controller. Since AngularJS services are lazy and singleton, this will only load the script once.

Resources