Load modules on demand (lazy load) in AngularJS
- Dependencies are automatically loaded
- Debugger like (no eval code)
- The ability to mix normal boot and load on demand
- Load via the service or the directive
- Use the embedded async loader or use your own (requireJS, ...)
- Load js (angular or not) / css / templates files
- Compatible with AngularJS 1.2.x and 1.3.x
-
Put ocLazyLoad.js into you project
-
Add the module
oc.lazyLoad
to your application -
Load on demand: With $ocLazyLoad you can load angular modules, but if you want to load controllers / services / filters / ... without defining a new module it's entirely possible, just use the name an existing module (your app name for example). There are multiple ways to use
$ocLazyLoad
to load your files, just choose the one that fits you the best.
You can include $ocLazyLoad
and use the function load
which returns a promise. It supports single dependency (object) or multiple dependencies (array of objects).
Load a single module with one file:
$ocLazyLoad.load({
name: 'TestModule',
files: ['testModule.js']
});
Load a single module with multiple files:
$ocLazyLoad.load({
name: 'TestModule',
files: ['testModule.js', 'testModuleCtrl.js', 'testModuleService.js']
});
Load multiple modules with one or more files:
$ocLazyLoad.load([{
name: 'TestModule',
files: ['testModule.js', 'testModuleCtrl.js', 'testModuleService.js']
},{
name: 'AnotherModule',
files: ['anotherModule.js']
}]);
You can also load external libs (not angular):
$ocLazyLoad.load([{
name: 'TestModule',
files: ['testModule.js', 'bower_components/bootstrap/dist/js/bootstrap.js']
},{
name: 'AnotherModule',
files: ['anotherModule.js']
}]);
If you don't load angular files at all, you don't need to define the module name:
$ocLazyLoad.load([{
files: ['bower_components/bootstrap/dist/js/bootstrap.js']
}]);
In fact, if you don't load an angular module, why bother with an object having a single files
property ? You can just pass the urls.
Single file:
$ocLazyLoad.load('bower_components/bootstrap/dist/js/bootstrap.js');
You can also load css and template files:
$ocLazyLoad.load([
'bower_components/bootstrap/dist/js/bootstrap.js',
'bower_components/bootstrap/dist/css/bootstrap.css',
'partials/template1.html'
]);
If you want to load templates, the template file should be an html (or htm) file with regular script templates. It looks like this:
<script type="text/ng-template" id="/tpl.html">
Content of the template.
</script>
You can put more than one template script in your template file, just make sure to use different ids:
<script type="text/ng-template" id="/tpl1.html">
Content of the first template.
</script>
<script type="text/ng-template" id="/tpl2.html">
Content of the second template.
</script>
There is two ways to define config options for the load function. You can use a second optional parameter that will define configs for all the modules that you will load, or you can define optional parameters to each module. For example, those are equivalents:
$ocLazyLoad.load([{
name: 'TestModule',
files: ['testModule.js', 'bower_components/bootstrap/dist/js/bootstrap.js'],
cache: false
},{
name: 'AnotherModule',
files: ['anotherModule.js']
cache: false
}]);
And
$ocLazyLoad.load([{
name: 'TestModule',
files: ['testModule.js', 'bower_components/bootstrap/dist/js/bootstrap.js']
},{
name: 'AnotherModule',
files: ['anotherModule.js']
}],
{cache: false});
If you load a template with the native template loader, you can use any parameter from the $http service (check: https://docs.angularjs.org/api/ng/service/$http#usage).
$ocLazyLoad.load(
['partials/template1.html', 'partials/template2.html'],
{cache: false, timeout: 5000}
);
The existing parameters that you can use are cache
and reconfig
.
The parameter cache: false
works for all native loaders (all requests are cached by default):
$ocLazyLoad.load({
name: 'TestModule',
cache: false,
files: ['testModule.js', 'bower_components/bootstrap/dist/js/bootstrap.js']
});
By default, if you reload a module, the config block won't be invoked again (because often it will lead to unexpected results). But if you really need to execute the config function again, use the parameter reconfig: true
:
$ocLazyLoad.load({
name: 'TestModule',
reconfig: true,
files: ['testModule.js', 'bower_components/bootstrap/dist/js/bootstrap.js']
});
The directive is very similar to the service. Use the same parameters:
<div oc-lazy-load="{name: 'TestModule', files: ['js/testModule.js', 'partials/lazyLoadTemplate.html']}"></div>
You can use variables to store parameters:
$scope.lazyLoadParams = {
name: 'TestModule',
files: [
'js/testModule.js',
'partials/lazyLoadTemplate.html'
]
};
<div oc-lazy-load="lazyLoadParams"></div>
As a convenience you can also load dependencies by placing a module definition in the dependency injection block of your module. This will only work for lazy loaded modules:
angular.module('MyModule', ['pascalprecht.translate', {
name: 'TestModule',
files: ['/components/TestModule/TestModule.js']
}, {
files: [
'/components/bootstrap/css/bootstrap.css',
'/components/bootstrap/js/bootstrap.js'
]
}]
)
You can configure the service provider $ocLazyLoadProvider
in the config function of your application:
angular.module('app').config(['$ocLazyLoadProvider', function($ocLazyLoadProvider) {
$ocLazyLoadProvider.config({
...
});
}]);
The options are:
-
jsLoader
: You can use your own async loader. The one provided with $ocLazyLoad is based on $script.js, but you can use requireJS or any other async loader that works with the following syntax:$ocLazyLoadProvider.config({ jsLoader: function(singleFile or [Array of files], callback); });
-
cssLoader
: you can also define your own css async loader. The rules and syntax are the same than for jsLoader.$ocLazyLoadProvider.config({ cssLoader: function(singleFile or [Array of files], callback); });
-
templatesLoader
: You can use your template loader. It's similar to thejsLoader
but it uses an optional config parameter$ocLazyLoadProvider.config({ cssLoader: function(singleFile or [Array of files], config, callback); });
-
debug
: $ocLazyLoad returns a promise that can be rejected if there is an error. If you set debug to true, $ocLazyLoad will also log all errors to the console.$ocLazyLoadProvider.config({ debug: true });
-
events
: $ocLazyLoad can broadcast an event when you load a module, a component and a file (js/css/template). It is disabled by default, set events to true to activate it. The events areocLazyLoad.moduleLoaded
,ocLazyLoad.componentLoaded
,ocLazyLoad.fileLoaded
.$ocLazyLoadProvider.config({ events: true });
$scope.$on('ocLazyLoad.moduleLoaded', function(e, module) { console.log('module loaded', module); });
-
loadedModules
: if you use angular.bootstrap(...) to launch your application, you need to define the main app module as a loaded module:angular.bootstrap(document.body, ['test']);
$ocLazyLoadProvider.config({ loadedModules: ['test'] });
-
modules
: predefine the configuration of your modules for a later use$ocLazyLoadProvider.config({ modules: [{ name: 'TestModule', files: ['js/TestModule.js'] }] });
$ocLazyLoad.load('TestModule'});
$ocLazyLoad
works well with routers and especially ui-router. Since it returns a promise, use the resolve property to make sure that your components are loaded before the view is resolved:
$stateProvider.state('index', {
url: "/", // root route
views: {
"lazyLoadView": {
controller: 'AppCtrl', // This view will use AppCtrl loaded below in the resolve
templateUrl: 'partials/main.html'
}
},
resolve: { // Any property in resolve should return a promise and is executed before the view is loaded
loadMyCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
// you can lazy load files for an existing module
return $ocLazyLoad.load({
name: 'app',
files: ['js/AppCtrl.js']
});
}]
}
});
If you have nested views, make sure to include the resolve from the parent to load your components in the right order:
$stateProvider.state('parent', {
url: "/",
resolve: {
loadMyService: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'app',
files: ['js/ServiceTest.js']
});
}]
}
})
.state('parent.child', {
resolve: {
test: ['loadMyService', '$ServiceTest', function(loadMyService, $ServiceTest) {
// you can use your service
$ServiceTest.doSomething();
}]
}
});
It also works for sibling resolves:
$stateProvider.state('index', {
url: "/",
resolve: {
loadMyService: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'app',
files: ['js/ServiceTest.js']
});
}],
test: ['loadMyService', '$serviceTest', function(loadMyService, $serviceTest) {
// you can use your service
$serviceTest.doSomething();
}]
}
});
Of course, if you want to use the loaded files immediately, you don't need to define two resolves, you can also use the injector (it works anywhere, not just in the router):
$stateProvider.state('index', {
url: "/",
resolve: {
loadMyService: ['$ocLazyLoad', '$injector', function($ocLazyLoad, $injector) {
return $ocLazyLoad.load({
name: 'app',
files: ['js/ServiceTest.js']
}).then(function() {
var $serviceTest = $injector.get("$serviceTest");
$serviceTest.doSomething();
});
}]
}
});
##Contribute If you want to get started and the docs are not enough, see the examples in the 'example' folder !
If you want to contribute, it would be really awesome to add some tests, or more examples :)