ocLazyLoad
ocLazyLoad copied to clipboard
Problem : ocLazyLoad with UI-Router and Webpack
Hi, I'm working on a project . i'm using ocLazyLoad and ui-router with webpack to lazyload module but it's not working.
angular 1.6
oclazyload 1.0.9
ui-router 0.3.2
i'm trying to resolve in my ui-router state. what i need to do ?
We have the same setup and it works when adding resolves to the route configuration like follows:
loadFileComponents: ($q, $ocLazyLoad) => {
return $q((resolve) => {
require.ensure([], () => {
let module = require('crud/file/components.js');
$ocLazyLoad.load({name: module.default.name});
resolve(module);
})
});
},
Hard to understand what is not working for you. Try to supply a plunkr or sample code.
how to handle this with a typescript file?
This should rather be on StackOverflow, but I hope this helps...
I don't remember where I took the example from, but the following TypeScript setup works for me, with future state definitions:
import { Ng1StateDeclaration, Transition } from '@uirouter/angularjs';
export const AdminStatePlaceholder: Ng1StateDeclaration = {
name: 'admin.**',
url: '/admin',
lazyLoad: function (transition: Transition) {
const $ocLazyLoad: oc.ILazyLoad = transition.injector().get('$ocLazyLoad');
return System.import(/*webpackChunkName:'chunks/admin'*/'../admin/admin.module')
.then(module => $ocLazyLoad.load(module.ADMIN_MODULE));
}
};
then in the main file: $stateRegistry.register(AdminStatePlaceholder)
and in the admin.module.ts I define $stateRegistry.register(AdminState)
which points to a fully featured state definition
Note: I use System.import
because ES dynamic import
s do not compile properly yet I believe, even with "module": "esnext"
in tsconfig.json file. That, plus the following to fix compiler errors:
// Monkey patch for tsconfig esnext imports
declare global {
interface System {
import (request: string): Promise<any>
}
var System: System
}
@kamilbrk thank you for your input. i did also use the "System.import" Regardless i have some other questions relating to your answers:
return System.import(/*webpackChunkName:'chunks/admin'*/'../admin/admin.module').then(module => $ocLazyLoad.load(module.ADMIN_MODULE));
where is the module name defined? is there a default name?
strange is also that sometimes the lazyloaded controller is loaded properly, and after some times the controller is again missing
vendor.js:274023 Error: [ng:areq] Argument 'myApp.controllers.schedulerCtrl' is not a function, got undefined
My Controller looks like this:
var schedulerModule = angular.module('schedulerModule', []);
module suiteApp.factories {
export class schedulerFactory {
public static $inject = ['$http'];
constructor( private $http: any) { }
someFunc = (mainScope) => {
//some code
}
}
schedulerModule.service("suiteApp.factories.schedulerFactory", schedulerFactory);
}
module suiteApp.controllers {
export class schedulerCtrl {
public static $inject = ['$scope'];
constructor( $scope: any ) {
//$scope.blah ...
//some code here
}
}
schedulerModule.controller("suiteApp.controllers.schedulerCtrl", schedulerCtrl);
}
now am i suposed to rewrite EVERY controller i lazyload to this? :
var mod = angular.module('foox', []);
mod.controller('suiteApp.controllers.schedulerCtrl', ['$scope', function () {
console.log("suiteApp.controllers.schedulerCtrl controller called");
}]);
module.exports = mod;
in order to work with ocLazyLoad? Thank you!
where is the module name defined? is there a default name?
Module name is defined in the lazily import
ed file.
Everything within the /admin
folder is in the chunks/admin
webpack chunk, only loaded on demand when accessing either /admin/**
URLs or admin.**
states.
This is using ui-router 1.x with folder-per-module pattern and - warning - it's an exhaustive example...
/src/app/index.ts
:
import * as angular from 'angular';
import { MAIN_MODULE } from './main/main.module';
angular.bootstrap(document, [MAIN_MODULE.name]);
/src/app/main/main.module.ts
:
import { module } from 'angular';
import uiRouter from '@uirouter/angularjs';
import 'oclazyload';
import { AdminStatePlaceholder } from './main.states';
export const MAIN_MODULE = module('app', [uiRouter, 'oc.lazyLoad']);
MAIN_MODULE.config(['$uiRouterProvider', function ($uiRouterProvider: UIRouter) {
const $stateRegistry = $uiRouterProvider.stateRegistry;
$stateRegistry.register(AdminStatePlaceholder); // <- register future state definition
}]);
/src/app/main/main.states.ts
:
import { Ng1StateDeclaration, Transition } from '@uirouter/angularjs';
export const AdminStatePlaceholder: Ng1StateDeclaration = { // <- future state definition
name: 'admin.**',
url: '/admin',
lazyLoad: function (transition: Transition) { // <- this is where magic happens
const $ocLazyLoad: oc.ILazyLoad = transition.injector().get('$ocLazyLoad'); // <- get hold of ocLazyLoad
return System.import(/*webpackChunkName:'chunks/admin'*/'../admin/admin.module') // <- let webpack know to split this into a separate chunk & dynamically import it
.then(module => $ocLazyLoad.load(module.ADMIN_MODULE)); // <- once it's loaded, add the angular module to runtime with ocLazyLoad
}
};
/src/app/admin/admin.module.ts
(lazy loaded in chunks/admin.js
):
import { module } from 'angular';
import { StateRegistry } from '@uirouter/angularjs';
import { GeneralComponent } from './general/general.component';
import { AdminState, GeneralState } from './admin.states';
export const ADMIN_MODULE = module('app.admin', []); // <- angular module name defined here
ADMIN_MODULE.component('general', GeneralComponent);
ADMIN_MODULE.config(['$stateRegistryProvider', function ($stateRegistry: StateRegistry) {
$stateRegistry.register(AdminState); // <- register the full state definition
$stateRegistry.register(GeneralState); // <- and others alongside it
}]);
/src/app/admin/admin.states.ts
(lazy loaded in chunks/admin.js
):
import { Ng1StateDeclaration } from '@uirouter/angularjs';
export const AdminState: Ng1StateDeclaration = { // <- full state definition
name: 'admin',
url: '/admin',
redirectTo: 'admin.general'
};
export const GeneralState: Ng1StateDeclaration = {
name: 'admin.general',
url: '/general',
component: 'general'
};
/src/app/admin/general/general.component.ts
(lazy loaded in chunks/admin.js
):
import { GeneralController } from './general.controller';
export const GeneralComponent = {
controller: GeneralController,
template: `This is general settings page.`
};
/src/app/admin/general/general.controller.ts
(lazy loaded in chunks/admin.js
):
export class GeneralController {
static $inject = ['$http'];
constructor (private $http: ng.IHttpService) {
console.log('GeneralController here');
}
}
I must say that this example is fairly immature. I am new to TypeScript and have given up on using namespace
pattern that I can see in your example (module suiteApp.controllers
) in favour of "modules": "esnext"
, import
/export
s and latest techniques for everything.
I spent way too much time, unsuccessfully, trying to sort out type definitions using latest version of TS and @typings
npm packages, but with "modules": "none"
. I'd probably recommend the same or stick to old-school setup without @types
.
now am i suposed to rewrite EVERY controller i lazyload to this? :
I am not sure if you would have to rewrite all files to the format you mentioned, but with my ES2015 TS+Babel setup I sure don't have any missing controller errors. I define my controllers and other elements per file and then import
them in the parent xyz.module.ts
file. I just have to make sure that export
/import
statements are in place and webpack sorts out everything else.
ocLazyLoad website has some links to webpack-based examples in the documentation section, I'd recommend to check them out.
@kamilbrk Thank you for the examples! Did you ever tried to lazyload typescript files via webpack? It works "--watch", but the problem with that is that when you update the typescript file (lazyloaded) it loads the js file (or it gets compiled), but after that when you change the Typescript file again, webpack recognize the change and starts build successfully, but the chunk is not recompiled it is still the same chunk ? Regards, Ben!
@kamilbrk when "ChunkManifestPlugin" is in use typescript chunks dont get updated, dont know why, but this explains my question above.