On-demand loading of AngularJS 1.x modules

Artem Kholodenko
Engineering @ BuildZoom
3 min readJan 12, 2017

--

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.

--

--