Meteor 1.5 dynamic imports with Angular (2+), Typescript

I’d like to ask whether anyone has thought about how the new dynamic imports will work with Angular and Typescript?
The key benefit I’m after is improved startup time as our app is growing rapidly but so is the load time.

I’m exploring a number of avenues and I thought it would try to clarify the key issues:

AOT Compilation of Angular templates.

Compilation of templates when the Angular app starts is definitely taking quite a bit of time for us, and the AOT compilation of those templates using ngc would help a lot. I know work is in progress to integrate this into the angular-meteor and angular2-compilers (see https://github.com/Urigo/angular-meteor/issues/1475 and https://github.com/barbatus/angular2-compiler/issues/2) and I really appreciate the efforts that the team have been putting into Angular support. But I also understand that they are quite busy with other things too.

Angular Modules

Angular has a sophisticated mechanism for grouping code together with dependencies. It is integrated with the router so the router can lazily load the modules on demand. See https://angular.io/docs/ts/latest/guide/ngmodule.html
This does work non-Meteor apps, but as you know meteor before 1.5 currently essentially bundles all client side code into one big download.

Typescript support for import()

It seems that Typescript doesn’t currently support the dynamic import() syntax, and I’m not sure what impact it would have on propagation of type information. Clearly at build time, for type checking the import would want to be eager, and then only actually do the require() etc. underneath at runtime. See https://github.com/Microsoft/TypeScript/issues/12364

More details on what sort of imports are supported are here: https://www.typescriptlang.org/docs/handbook/modules.html#import
Though Typescript does have some support for calling require() so possibly there’s some way of using that instead. https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require

So my question is really more a case of:

Has anyone considered how these issues interact with Meteor 1.5?

Does anyone have any additional information towards solving these?

Here are my related thoughts…

For me, Typescript is has been a real benefit maintaining a larger code base with multiple people working on it. It naturally fits with Angular (2+) and given our investment in both it would be a real shame and significant effort to lose either.
We also rely heavily on meteor methods, subscriptions, account package plus lots of other nice Meteor things, e.g. server side coding with fibers is nice too (though async/wait would be fine), therefore we’re not keen to lose Meteor either.

Meteor 1.5 seems to be going in a fantastic direction - finer grained lazy loading and caching seem to be exactly what we need (though it obviously won’t solve the template compilation issue). With that, we would not need to rely on Angular modules for bundling.

So, I thought I’d ask for input.

Please avoid non-constructive “switch to React (or Vue, or …)” or “ditch Typescript” suggestions. These are a consideration but the switch cost is pretty high in a number of respects, and I’d like to explore options with the existing stack first.

UPDATE: I did come across this post from Uri about the Meteor Client Bundler which may help. I haven’t absorbed/investigated it all just yet: https://blog.meteor.com/leverage-the-power-of-meteor-with-any-client-side-framework-bfb909141008

I had the same problem with meteor + angular 2 . Angular 2 is slow at rendering and has huge framework size even when gzipped . So i had to move from angular 2 to aurelia . Aurelia is so much simpler and faster and also i am devoloping client side separetly from meteor. Using ddp websocket code that i write for communicating with meteor server . That way is 4 times faster than old version

I’ve also been struggling with understanding the current state of meteor / angular / typescript / AOT / lazy loading modules.

I can mention that there is a workaround mentioned in the 1.5 PR that shows how the import() typescript issue can be solved. Basically by adding the dynamic-imports package and using module.dynamicImport(‘path/to/module’) instead of import(‘path/to/module’). This returns a promise however so I’ve had to use it like this in order to lazy load Angular routes:

const lazyProfile = () => {
  return new Promise((resolve) => {
    module.dynamicImport('../features/profile/view/profile.module').then( i => {
      resolve(i.ProfileModule);
    });
  });
};

export const appRoutes: Route[] = [
  {
    path: ':slug',
    component: ProfilePage,
    loadChildren: lazyProfile
  }
];

Not sure if this is the best way to do it but I’m waiting to see something else…

As far as AOT compilation goes: as you’ve pointed out there is the currently available solution that @Urigo posted in his blog post using the meteor build tool. Of course, this isn’t ideal if your app is already built within Meteor. The only other option that is on the horizon is the new angular-compiler package @barbatus is working on. I’m hoping that will be released soon as it seems like the best solution in my case.

Many thanks @jdreckley, that gives me a number of things to try out - it is comforting that albeit with some promise shenanigans someone has got lazy loading to work in Angular+TS+Meteor1.5!

Does the dynamic import inline work? Like this:

loadChildren: () => module.dynamicImport('../path/profile.module').then(i => i.ProfileModule)

Or did you need the wrapper to get it to work? (e.g. Promise library clashes)

You can possibly simplify the wrapper that lazily loads the module by using the default export in the module being loaded. That should avoid the need for the i.ProfileModule dereference. Possibly reducing it to:

loadChildren: () => module.dynamicImport('../path/profile.module')

There’s also a possibility that you could gets some benefits from using a custom module loader (example here). But my guess is that the Meteor code that identifies lazy imports would not be able to ‘see’ the import if you hid it completely inside a custom loader.

From separate discussions I understand that the all-in-one compiler is pretty much there, so I’m going to try that as well to get the templates AOT compiled.

That doesn’t work for me but I’ve made a simple example if anyone wants to try.

Thanks for that @jdreckley the example really helped me get started with it.

I forked it to start playing around with, and discovered that I needed to make some tweaks to get the npm modules loaded and avoid errors. You can see my fork and the changes I made.

I found that I could avoid the extra Promise wrapper, and that seems to work fine (change) resulting in:

loadChildren: () => module.dynamicImport('../expensive/expensive.module').then(m => m.ExpensiveModule)

I also got the default export form working (here) though it is debatable how much it is better. The router reference is a touch simpler (no need to have the untype-checked module name), but you have to remember to add the default prefix to the lazy module.
When AOT compiled I think we may need to include the ngfactory version of the module so it may not help in the long run.

Nice! I was able to replicate that as well.

Nice observation @sdarnell Couldn’t put it better. We have the identical considerations on our project that is using the same teh stack. Would be really nice to know if AOT is on the roadmap in the near future. We are experiencing drastically slow loading time on Android too. I’m guessing AOT could help there since lazy load does not help when all the code is in apk already.

AOT is in the works, and https://github.com/barbatus/angular2-compiler contains an ‘all-in-one’ compiler that appears to support AOT. I’ve not had enough time yet to try it out but hope to do so soon.

@kombadzomba As for you comment about lazy loading not helping if embedded in the APK - clearly you won’t get any benefits from reduced network load times, but I suspect you may see two other significant benefits.
Firstly, the template compiler won’t even try to compile modules that haven’t been loaded - so you should see some benefit by delaying the template compilation.
Secondly, you may also see some benefits in reduced memory use (as less code is loaded), and reduced Javascript parsing overheads.

I’ve made an example:

About AoT compilation and lazy loading.
Angular’s AoT generates .ngfactory.ts files for each lazy loaded NgModule and it also uses import statements of ES Modules (@angular/core/some/module). Because of this, we end up with whole @angular/core being loaded and every single ES Module that has been requested. So for now, it’s a huge issue, but maybe I’m out of date with this.

Looks like Typescript 2.4 will include syntax support for dynamic import().
https://blogs.msdn.microsoft.com/typescript/2017/06/12/announcing-typescript-2-4-rc/

Since we were talking about AOT on mobile devices I would like to share my experience…

Unfortunately we couldn’t wait for barbatus-compiler polish so we decided to implement separation of angular from meteor following this: https://blog.meteor.com/leverage-the-power-of-meteor-with-any-client-side-framework-bfb909141008 so we could experience the benefits of angular AOT with meteor.

What we saw was that even plan cordova project loaded with AOT bundle starts on android in 3 seconds compared to 20 as it was before, in browsers similar. There is no lazy loading at all. My conclusion is that lazy loading without AOT doesn’t help this significantly and we tried lazy loading too. Compilation is the killer when using angular. So the AOT should be more important than lazy loading for angular+meteor in my opinion.

We’ve released angular2-meteor version 0.8.0 to support the latest versions of Angular (4 and 5).
Thanks to this PR from @darkbasic .

Just a note, that we’ve updated this API even though we consider it a deprecated API, and you should probably slowly migrate your data solution to meteor-rxjs.

we’ve also published new angular-compilers package that supports all the features the Angular CLI gives you:

  • Typescript compilation
  • Assets compilation (HTML, styles)
  • Angular’s AoT compilation
  • Rollup’s bundling with Tree Shaking

and added a lot of examples with for all those features plus an example with Angular CLI if you want to use it instead of the Meteor CLI:

We’ve also updated all the latest versions including the latest Angular 5 and Meteor 1.6.

Please open a new issue if you encountered one.

We’ve updated the Ionic CLI tutorial and in the process of updating the Meteor CLI one.