Valor Software is not only a software development company but also a great contributor to the Angular development community: being creators of ngx-bootstrap, we strive to improve our beloved library as Angular innovates. This time we worked on implementing Ivy renderer. Ivy is a complete rewrite of the renderer with the significant refactoring of the compiler enabling devs to:

  • Reach better build times (thanks to incremental compilation)

  • Make a bundle smaller and faster, which will be especially useful for mobiles (with a generated code more compatible with tree shaking)

  • Unlock new potential features (metaprogramming or higher order components, lazy loading of the component instead of modules, a new change detection system that is not based on zone.js, etc.)

Road To Success: Implementing Ivy

According to the promising features above, we’ve had two goals in mind:

  1. Make ngx-bootstrap compatible with other projects, which are using ngx-bootstrap and ivy:enabled.&nbsp

  2. Make all of our demos ivy:enabled-compatible.

In total, we spent about five days to achieve our goals with Ivy support (1 developer + 1 QA engineer).

However, implementation was not an easy task. The first issue which we faced is that Ivy-ngcc doesn’t support the spread operator inside @NgModule. After removing it, we’ve achieved our first goal and ngx-bootstrap began to support Ivy from version 5.1.0.

Ok, that’s better already. But we also have to update our demo app to Ivy compatible format. It was a bit harder to achieve: we faced several issues. Let’s go through them one by one:

  1. The host should not return a redirect source file from 'getSourceFile' (related Angular Issue)

In the Angular 8, they’ve changed the TypeScript output to ES6, so instead of using this.hostAdapterwe now need to use this.hostAdapter when searching for the line number.

Unfortunately, the Angular team still hasn’t proceeded to resolve the issue. So we had to temporarily monkey patch Angular to fix the issue.

Fix for:The host should not return a redirect source file from 'getSourceFile'^
  1. ERROR in demo/src/__ng_typecheck__.ts(857,9): error TS2551: Property 'innerHtml' does not exist on type 'HTMLParagraphElement'.

The root cause of the second issue is that 'innerHtml' binding got broken since Angular 8.0.0, as Angular 7 had no strict check for templates.

To fix this, we’ve changed all occurrences of innerHtml to innerHTML.

  1. Injecting the deprecated renderer (Renderer) is not supported, so we had to use Renderer2.

  1. Can’t bind to 'ngClass'. Can’t bind to 'ngIf'

Our library and demo application (which uses this library) live in a mono repo, so for the demo application to use the ngx-bootstrap library as if it were installed from npm packages, we had to link our dist (compiled library) with the node_modules folder (named ngx-bootstrap)

However, unfortunately, the dist binding to node_modules cannot be handled by Ivy, so the compiled ngx-bootstrap must be placed directly inside the node_modules.

However, unfortunately, the dist binding to node_modules cannot be handled by Ivy, so the compiled ngx-bootstrap must be placed directly inside the node_modules.

In our case, this caused the following errors:

  • Can’t bind to 'ngClass' since it isn’t a known property.

  • Can’t bind to 'ngIf' since it isn’t a known property.

We dealt with those two by writing a one-liner that copies the entire folder and renames it.

  1. For some reason, 'host: style' option of decorator @Component became rewritten by the same option from the class from which we inherit. This is not described well in the official documentation. As a solution, we directly updated the host element styles using the renderer method.

  1. Issues with Unit Tests

We pay great attention to unit tests, and we mean it! We run tests for different browsers, operating systems, and we even run them for different Angular versions. So we wanted not only to fix all of them but also add separate stages with tests into our CI, which will check ngx-bootstrap with Ivy:enabled and disabled, respectively. Unit tests help us to make sure that we’re delivering the qualitative library to a massive audience of users.

In the process of implementing Ivy, we’ve noticed that event triggering inside our tests is not working as expected:

const directive = fixture.debugElement.query( By.directive(PopoverDirective));directive.triggerEventHandler('click', {});

To resolve the issue, we used the cross-browser library for testing @netbasal/spectator.

Show Me the Numbers!

Let’s see how we can benefit from using Ivy instead of the View Engine. For starters, we’ve tested bundle size reducement.

Compiled to ES5

After updating ngx-bootstrap with Ivy support, we have reduced a bundle size by 12%.

Compiled to ES5: Bundle size reduced by 12%^

Built with the View Engine:

Chunks sizes for application
sizes for application

Built with Ivy:

Chunks sizes for application
Modules sizes for application

Compiled to ES6

After building with Ivy, we have reduced a bundle by 13%.

Compiled to ES6: Bundle size reduced by 13%^

Built with the View Engine:

Chunks sizes for application
sizes for application

Built with Ivy:

Chunks sizes for application
Modules sizes for application

Unfortunately, after enabling server-side compression, Ivy didn’t show us any signs of improvement. In general, the gzipped bundle size increased by 2%. If it has to do with tree shaking not working correctly or not, we hope for this to be updated in the final Ivy version.

Gzipped: Bundle size increased by 2%^
Gzipped bundle size gained 2% after compilation with Ivy.

What’s next

The Angular core team is still working hard on Ivy. Therefore, we are confident that soon, all the issues we faced with Ivy will be fixed and official documentation will be shared and updated accordingly.

Still, there’s no need to wait for the final release to start using Ivy-enabled ngx-bootstrap - we’ve releasedversion 5.1.0!

Vitaliy Makogon
JavaScript Developer
https://twitter.com/mVitaliyd

Ludmila Nesvitiy
QA Automation Engineer
https://twitter.com/LudmilaNes