On-demand loading of AngularJS 1.x modules
Single page client-side apps can quickly become bulky and full of niche dependencies. Services and components needed by non-core features start to bloat the script size and increase load times. Below is a guide based on an experiment I performed for on-demand loading AngularJS 1.x modules and leveraging the functionality of a service of the module.
Create module and service
The service you want to load on demand should be declared in it’s own module. In the simple example below, I declare an alerter
module and as part of the module an alerterService
in a file named alerter_module.js
.
angular.module('alerter', []);angular.module('alerter').service('alerterService', [function () {
return {
showAlert: function (message) {
alert(message);
}
};
}]);
Load module via AJAX
For a simple example assume you have a user
module with a userButtons
component that needs to show an alert when a button is clicked.
angular.module('user', []);angular.module('user').component('userButtons', {
template: '
<button ng-click="$ctrl.onAlert(\'Good!\')">Good</button>
<button ng-click="$ctrl.onAlert(\'Bad!\')">Bad</button>',
controller: [function () {
this.$onInit = function () {
this.alerterService = null;
};
this.onAlert = function (message) {
if(this.alerterService) {
this.alerterService.showAlert(message);
}
else {
this.loadAlerter(message);
}
};
this.loadAlerter = function (message) {
// ... details below
};
}];
});
The first time a button is clicked this.alertService
is null
. This leads to this.loadAlerter(message)
being called.
Inside this.loadAlerter()
, load the JS file containing the alerter
module and related code. I’m using jQuery’s $.getScript()
shortcut method in this example. The script tag creation can also be done manually using vanilla JS. This action pulls the code remotely and makes available on the page.
$.getScript('alerter_module.js', function () {
// ...
});
Now, add the alerter
module to the array of modules injected into the module where you want to use alerter
functionality.
$.getScript('alerter_module.js', function () {
// adding 'alerter' to array of module dependencies
// injected into 'user' module
angular.module('user').requires.push('alerter');
});
Then, get a reference to the alerter
module:
$.getScript('alerter_module.js', function () {
angular.module('user').requires.push('alerter'); // get reference to 'alerter' module to
// get access to its services
var injector = angular.injector(['ng','alerter']);
});
Then, get a reference to the alerterService
that you want to leverage and call a method of the service. Remember to bind the scope of the component to the callback, to access this.alerterService
.
$.getScript('alerter_module.js', function () {
angular.module('user').requires.push('alerter');
var injector = angular.injector(['ng','alerter']);
// get reference to service of module
this.alerterService = injector.get('alerterService');
// call method of service
this.alerterService.showAlert(message);
}.bind(this));
Putting it all together:
angular.module('user', []);angular.module('user').component('userButtons', {
template: '
<button ng-click="$ctrl.onAlert(\'Good!\')">Good</button>
<button ng-click="$ctrl.onAlert(\'Bad!\')">Bad</button>',
controller: [function () {
this.$onInit = function () {
this.alerterService = null;
};
this.onAlert = function (message) {
if(this.alerterService) {
this.alerterService.showAlert(message);
}
else {
this.loadAlerter(message);
}
};
this.loadAlerter = function (message) {
$.getScript('alerter_module.js', function () {
angular.module('user').requires.push('alerter');
var injector = angular.injector(['ng','alerter']);
this.alerterService = injector.get('alerterService');
this.alerterService.showAlert(message);
}.bind(this));
};
}];
});
Conclusion
The above example demonstrates the ability to load and inject angular modules on demand. This minimizes the code loaded as part of the initial page load. The approach also forces the separation of non-critical code into modules for better decoupling and maintenance.
Note: In addition to using services, it’s possible to $compile and add to DOM components and any other pieces of a loaded module.