Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support passing in module to lazy-load as a callback in addition to s… #3037

Merged

Conversation

dtabuenc
Copy link
Contributor

…tring

This enables dealing with the loading of modules with a custom callback thus skipping
the default NgModuleLoader. This is a must-have in order to enable lazy-loading
using webpack.

…tring

This enables dealing with the loading of modules with a custom callback thus skipping
the default NgModuleLoader. This is a must-have in order to enable lazy-loading
using webpack.
@christopherthielen
Copy link
Contributor

Looks about right, thanks!

@christopherthielen christopherthielen added this to the 1.0.0-final milestone Sep 26, 2016
@amcdnl
Copy link
Contributor

amcdnl commented Sep 26, 2016

Also a must for using w/ ocLazyLoad too :)

@christopherthielen
Copy link
Contributor

Also a must for using w/ ocLazyLoad too :)

loadNgModule is for loading an NgModule, i.e., angular 2. for oclazyload you can use it directly

.state({
  name: foo,
  lazyLoad: transition => {
    let $ocLazyLoad = transition.injector().get('$ocLazyLoad')
    return $ocLazyLoad.load('ng1Module');
  }
});

@amcdnl
Copy link
Contributor

amcdnl commented Sep 26, 2016

@dtabuenc Whats the usage of this look like? I get:

stateService.ts:531 Error: No NgModule metadata found for '[object Object]'.
    at NgModuleResolver.resolve (ng_module_resolver.ts:32)
    at CompileMetadataResolver.getNgModuleMetadata (metadata_resolver.ts:216)
    at RuntimeCompiler._compileComponents (runtime_compiler.ts:136)
    at RuntimeCompiler._compileModuleAndComponents (runtime_compiler.ts:70)
    at RuntimeCompiler.compileModuleAsync (runtime_compiler.ts:56)
    at ModuleBoundCompiler.compileModuleAsync (runtime_compiler.ts:403)
    at lazyAdapter.js:65
    at run (es6.promise.js:87)
    at es6.promise.js:100
    at MutationObserver.flush (_microtask.js:18)

digging in here:

return offlineMode ? loadChildrenPromise : loadChildrenPromise.then(moduleType => {
        console.log('--- compiling ng2 module ---', moduleType);
        compiler.compileModuleAsync(moduleType);
      })

I found that the import is expecting the type out whereas with:

{
    name: 'admin',
    url: '/admin',
    lazyLoad: loadNgModule(() => System.import('app/admin/admin.module.js'))
  },

its returning an es module. so if your import was like:

@NgModule({
  imports: [
    BrowserModule,
    UIRouterModule.forChild({ states: ADMIN_STATES })
  ],
  declarations: [AdminComponent, ResetComponent]
})
export class AdminModule { }

it would be returning { AdminModule } instead of the pure module. I'm thinking you might have to either export default or have a more complex System.import.

@christopherthielen
Copy link
Contributor

I'm thinking you might have to either export default or have a more complex System.import.

I'm ok with this. v3 router also requires a lazy loaded NgModule to be exported as export default

@amcdnl
Copy link
Contributor

amcdnl commented Sep 26, 2016

I changed this function and was to get it to work using default...

   const loadModuleFactory = (loadChildren, ng2Injector) =>{
     if(typeof(loadChildren) === 'string'){
       return ng2Injector.get(NgModuleFactoryLoader).load(loadChildren);
     } else {
      const compiler: Compiler = ng2Injector.get(Compiler);
      return loadChildren.then(moduleType => {
        return compiler.compileModuleAsync(moduleType.default);
      });
     }
   }

@amcdnl
Copy link
Contributor

amcdnl commented Sep 26, 2016

Can we rename loadNgModule to loadNg2Module? :)

@dtabuenc
Copy link
Contributor Author

To use this, the callback has to return a type or a promise to a type, not an object. If using something such as webpack2 and es6 module syntax that means you have to get the "default" property. If you are using webpack1 and just a standard require('path') call then you would not need anything else.

One way to do it in webpack 2 is just chiain the promise:

loadNgModule(()=>System.import('modulePath').then( module=>module.default);

An alternative (used in the angualr2 webpack starter project) is to use the string replace plugin for webpack to automatically convert the System.import:

   {
               test: /\.ts$/,
               loader: 'string-replace-loader',
               query: {
                 search: '(System|SystemJS)(.*[\\n\\r]\\s*\\.|\\.)import\\((.+?)\\)',
                 replace: '$1.import($3).then(mod => (mod.__esModule && mod.default) ? mod.default : mod)',
                 flags: 'g'
               }
 }

This would then allow you to do away with the extra chained then:

loadNgModule(()=>System.import('modulePath'));

@dtabuenc
Copy link
Contributor Author

The syntax is also exactly the same as v3 component router's loadChildren property. The only difference is this only currently supports promises while the v3 router additionally supports observables.

@amcdnl
Copy link
Contributor

amcdnl commented Sep 27, 2016

@dtabuenc - that syntax is rough :S ... it would be nice if we could auto-resolve if it had a exported default ... a webpack plugin solution seems a tad excessive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants