ocLazyLoad icon indicating copy to clipboard operation
ocLazyLoad copied to clipboard

Problem : ocLazyLoad with UI-Router and Webpack

Open ghost opened this issue 8 years ago • 7 comments

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 ?

ghost avatar Dec 11 '16 14:12 ghost

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.

motin avatar Feb 20 '17 11:02 motin

how to handle this with a typescript file?

AGuyCoding avatar Jul 14 '17 07:07 AGuyCoding

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 imports 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 avatar Jul 19 '17 12:07 kamilbrk

@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!

AGuyCoding avatar Jul 21 '17 12:07 AGuyCoding

where is the module name defined? is there a default name?

Module name is defined in the lazily imported 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/exports 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 avatar Jul 21 '17 15:07 kamilbrk

@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!

AGuyCoding avatar Jul 24 '17 11:07 AGuyCoding

@kamilbrk when "ChunkManifestPlugin" is in use typescript chunks dont get updated, dont know why, but this explains my question above.

AGuyCoding avatar Jul 26 '17 13:07 AGuyCoding