1

I'm looking for a way to lazy load a module that doesn't contains any components but services only. The background: in my app is an Excel export service using exceljs library. This library is extremely large and I'm trying to prevent that the library will become part of vendor.js or main.js. It is "included" via import * as Excel from 'exceljs/dist/exceljs.min.js' in a service.

Now I have several components in separate modules that make use of the "Excel export" service (no component). I would prefer a method that the service is only loaded if the user is "clicking the export button" but not before.

How this can be done?

Storytellerr
  • 642
  • 3
  • 18
Lars
  • 920
  • 1
  • 14
  • 34
  • Have you tried adding your service to a module and then load the module with `import(path/to/module).then(...)` ? – Andrei Gătej Aug 08 '20 at 10:44
  • @AndreiGătej where and how should I put this piece of code. Please can you give a bit extended example? – Lars Aug 08 '20 at 11:28
  • @AndreiGătej ok now I understood what you wrote with import(...). But the question is: how can I get access of a variable from the imported files, like ìmport { exceljs } from 'pathtoexceljs.min.js`? – Lars Aug 08 '20 at 12:40
  • I’ll provide an example a bit later. But until then, here’s my reasoning: you have a service which imports that excel lib(which is large). You’d then create a module in which you will only import this service. Then, for example, when a button is clicked, in the handler you’d have ‘import(path-to-excel-module).then()...’. In the then’s callback you should be able to compile the module or something like that, I haven’t done this in a while, but I’m sure you can access the module’s imports from there – Andrei Gătej Aug 08 '20 at 12:52
  • Thank you so much! Currently, I was able to load a JS file (CDN https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.1.1/exceljs.min.js). But I'm unable (I don't now how) to access variables or functions. Without this "lacy load" I do: import Excel from 'exceljs/dist/exceljs.min.js' and later const wb = new Excel.Workbook(); But in the "lacy mode" I have no idea how I can access "Excel" because it is always undefined. – Lars Aug 08 '20 at 13:06

1 Answers1

1

Here would be an approach:

excel.module.ts

@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ],
  providers: [ExcelService]
})
export class ExcelModule {
  static loaded = false;
}

app.component.ts

export class AppComponent {
  constructor (
    private compiler: Compiler,
    private injector: Injector
  ) { }
  
  load () {
    import('./excel/excel.module')
      .then(m => m.ExcelModule)
      .then(m => {
        console.dir(m.loaded)
        
        return m;
      })
      .then(m => this.compiler.compileModuleAsync(m).then(r => (m.loaded = true,r)))
      .then(factory => {
        const module = factory.create(this.injector);
        
        console.dir(module.injector.get(ExcelService))
      })
  }
}

The first time is loaded, m.loaded will be false. On subsequent times, it will be true. With this, we can be sure that we don't load the module multiple times.

Of course, a easier (and probably better) approach will be to have a dedicated service for loading modules e.g ModuleLoaderService, where you'd have a Map object keeping track of loaded modules.

ng-run demo.

Andrei Gătej
  • 11,116
  • 1
  • 14
  • 31