Here’s why you should be excited about Ivy!

An overview of the features Ivy brings us today, and a look at possible features Ivy enables in future versions of Angular.

Kevin Kreuzer
Feb 4 · 13 min read
Image for post
Image for post

Recently, during coffee, I had a chat with a developer about Angular 9. I told him how excited I am for the upcoming release. In particular, because of Ivy.

Even though he already played around with Ivy in version 8, he couldn’t understand my excitement.

“All that hype for a bit smaller bundles!”

Well, there’s a lot more to it. Let’s explore why I am excited about Ivy and why you should be too?

Yes, we get smaller bundles

Image for post
Image for post
Bundle size statistics presented at the last ng-conf

Switching to Ivy, we get smaller bundles for free! Pretty cool. But how come? Well. Because we call Ivy!

The big idea behind Ivy: Inverting

The ViewEngine takes all the Angular features and iterates our code through. Based on the features we use, Angular executes the required framework code.

Ivy inverts the behavior of the ViewEngine.

Ivy, on the other hand, compiles our HTML into JavaScript instructions, which pull in the Angular capabilities.

Image for post
Image for post
The ViewEngine calls us; we call Ivy

Let’s analyze this behavior further by having a look at an Ivy compiled template of an Angular component.

Image for post
Image for post

Ivy keeps our Component code and adds a factory to it. Furthermore, it adds all the necessary component metadata like selectors, inputs, outputs etc. All metadata remain local to the component (Locality).

The most exciting part, however, lies in the template function. The template function renders our DOM. It contains two phases, which are represented by the rf parameter. In the first phase, the template is built up, the elements are written into the page. In the second phase, the template bindings are resolved.

The code inside the template function creates the DOM by calling Ivy instructions. This significant change over the ViewEngine is the one that improves our bundle size. How come?

“If you want to enable tree shaking you need to be explicit about your intents”

By executing Ivy instructions, our code is very explicit about it’s intends and can, therefore, be analyzed by the tree shaker. The tree shaker now knows which parts of Angular we are going to use and which not.

Thanks to this change, the tree shaking mechanism can shake out the unsued parts. In a nutshell, Angular itself becomes tree shakable.

Okay, that’s cool. But so far, we only saw bundle size improvement. (Which, by the way, in my opinion, is already enough to be pretty excited about Ivy! 😉)

Image for post
Image for post

Lazy loading components with Ivy 🌱

Lazy loading a component? 🤔 You mean, lazy loading modules, didn’t you? No, I mean lazy loading components.

In current Angular versions, modules can be lazy-loaded with the help of the Angular router. Once a user hits a lazy loaded route, Angular loads the required chunk of code in a separate request.

Currently, it’s only possible to lazy load on a module level. With Ivy, we get more fine granular control over lazy loading.

Imagine we have a Quiz application. We want to dynamically load the QuizCardComponent once the user starts the Quiz.

Image for post
Image for post
lazyload a component and dynamically add it to a ViewContainer

Once the user clicks on “Start quiz” we go on and fetch our QuizCardComponent. Once loaded, we dynamically add the component to a container. From here on, we can interact with the component.

With Ivy, lazy loading a component can be achieved by using the EcmaScript import syntax.

Image for post
Image for post
The simplest way to lazy load a component in Angular 9 with Ivy

The import statement returns us a Promise. Once this Promise resolves, we get a hold of the component instance.

That’s the overall idea. Of course, with the snippet above, you can’t yet implement a productive lazy-loaded component. There’s more to it especially once you start to use third party components like Angular material.

Don’t worry. If you want to find out how to write a productive lazy-loaded component that uses Angular material, check out my article on “Lazy load components in Angular”.

Maybe you think; Pretty cool. But how often will I use this feature? Lazy loading on a module level is already pretty good.

Okay. I see you are not yet thoroughly excited. Let’s have a look at the next feature Ivy offers us today.

Improved debugging — the flying ng

Ivy exposes an ng object. This object “flies” around in our console. We can use it at runtime to get a component instance. Once we got hold of a specific component instance, we can start to manipulate it.

To do so, we open up our DOM explorer and use the arrow symbol on the top left corner of our dev tools to select the desired component.

We can use $0 to get a reference to the current DOM node. By using ng.getComponent($0) we get a hold of our component.

Image for post
Image for post
Use the ng object to manipulate components at runtime

In this Quiz game, the user has to guess the city. The displayed city above is Bern. But we manipulate our QuizCardComponent with the ng tools and change the correctAnswer on the QuizCardComponent to “Dublin”.

When we click on Bern, we can see that the QuizCardComponent shows a red color to indicated that our answer was wrong, and it displays the correct answer, Dublin (which is not correct).

We can manipulate our components at runtime in the browser! Pretty nice. Are you already excited about Ivy? No? The best is yet to come! The possibilities Ivy offers for the future!

Image for post
Image for post

So far, we talked about features that we can already use today. In my opinion, the most significant thing about Ivy is the possibilities it offers for the future.

Ivy is the enabler for the future of Angular

⚠️ Warning! All the APIs used in the examples are private or so-called “thrill seeker” APIs. They aren’t yet officially supported APIs. Their future is unclear. They may or may not land in future versions of Angular.

ɵrenderComponent

A “Thrill seekers” API is an API that is experimental and not yet finalized. Its also not yet clear if it will be finalized in future versions or not. All thrill seeker APIs in Angular use a delta prefix (ɵ).

Even though the “thrill-seekers” APIs aren’t yet finalized, we can already use them and play around with them. Since they contain a delta prefix(ɵ), which, by the way, nobody knows how to type on their keyboard, 😉 I recommend using an import alias.

import {ɵrenderComponent as renderComponent} from '@angular/core';

Ok. Let’s have a look at what we can do with renderComponent. Well, we can render a component. 😊

Usually, we start our Angular application with the bootstrapModule function. Let’s comment on the current bootstrap approach and use renderComponent instead.

Image for post
Image for post
Bootstrapping of a single component without the usage of a Module

If we run ng serve we won’t notice a difference; everything works as before. Even without a NgModule. A considerable difference though lies in our bundle size which now is down to 23 KB. Gzipped we would even land under 10 KB.

renderComponent is the way to achieve really small bundle sizes.

But how does renderComponent know where to render our AppComponent?

Well, we still have a selector in our DOM, we are using the <app-root> tag in our index.html. If we have such a selectorrenderComponent renders the component directly to the selector.

ɵrenderComponent in combination with lazy loading

Image for post
Image for post
lazyload a component and render it using renderComponent

renderComponent accepts an additional opts property. Those options allow us to pass the host where our component should be attached and also an injector so that our component can take advantage of DI.

This is important when we lazy load our component. In a lazy-loaded scenario, we don’t have a selector in our DOM; therefore we need the host property.

ɵrenderComponent for Web components

Let’s quickly have a look at how we could deliver a simple switch as a web component.

Image for post
Image for post
Simple switch component

To deliver this component as a web component, Ivy allows us to write an element’s class for this.

Image for post
Image for post
Element wrapper for the switch component

The SliderElement class extends HTMLElement. Once the constructor of this class is executed, we use the renderComponent method to tell Ivy that it should render our SliderComponent to this CustomElement.

The renderComponent method returns us a component instance. We use this instance to react on @Outputs or to pass @Inputs to our component. Passing Inputs is straight forward, however, when we react on @Outputs we subscribe to the stream and use the dispatchEvent method to dispatch a CustomEvent.

Once we have set up our SliderElement we still need to define it as a custom element in our main.ts.

customElements.define('my-slider-element', SliderElement);

Great! We are now able to interact with our Web component using plain HTML. If we can interact with a component using standard DOM APIs, we are guaranteed that we can also interact with those components using other frameworks.

Image for post
Image for post
Interact with a custom element using standard DOM APIs

What about NgModules? 📦

To answer this question, let’s have a look at a component compiled with Ivy.

Image for post
Image for post
Ivy metadata uses directives to specify dependencies to other components

Earlier, we have seen that Ivy introduces a concept called locality. This means all the metadata are local to the component. In other words, Ivy does not care about NgModule. Ivy uses the directives metadata to know about the dependencies of a component.

directives: [_hello_component__WEBPACK_IMPORTED_MODULE_1__["HelloComponent"]],

Currently, Ivy knows about this because it extracts the information from the NgModule. In future versions, however, Angular could have a public API to declare dependencies directly on the component. There are multiple proposals out there.

1. Grouping components in an array

Image for post
Image for post
Directly assign the dependencies to the components directives property.

Lars Gyrup Brink Nielsen gave a great workshop about optional NgModules. I recommend you to check it out:

Specify Deps on the component

Image for post
Image for post
Specify the dependencies of a component on the components decorator

Again, those are proposals, and it’s not yet sure if and how they land in Angular.

Higher-order components

Higher-order functions are probably the easiest way to explain this concept:

Higher-order functions are functions that take other functions as arguments or return functions as their results.

A well-known example of a higher-order function is the map function on arrays. If we would implement the map function on our own, we perform the mapping according to a projection function which we accept as a parameter.

Image for post
Image for post
Custom implementation of a map function

Higher-order functions allow us to use composition. We can create multiple small functions and compose them to a more complex function.

The same concept applies to components. Higher-order components are components that return new components. Let’s have a look at a use case where higher-order components would make sense.

Let’s start with a dumb component that displays a name.

Image for post
Image for post
A dumb component that displays a name

It’s a dumb component because it does not care where the name comes from, it only shows it.

Dumb components are an essential part of every Angular application. They only care about displaying @Inputs and emitting @Outputs. But alone they are not very useful. Usually, dumb components are used inside a smart component.

What if we want to use the HelloComponent in multiple places where it would need to access the name from the QueryParams in the route. Maybe we even have a PriceComponent which does the same but obtains the price from the price QueryParam instead of the name QueryParam. We would need to write a lot of duplicated code.

The idea of a higher order component can help us here.

Image for post
Image for post
Pseudocode that illustrates the idea of a higher-order component

We have a function called displayQueryParam which accepts a component. Inside this function, we create our higher-order component. In the ngOnInit life cycle hook of this component, we read all the QueryParams.

The most exciting part is then happening inside the template function. In the template function, we would dynamically create an instance of the component we passed to the displayQueryParam method. Next, we would use the component's metadata to check if one of the QueryParams matches an @Input property. If so, we would pass that value to the Component.

Since this is not officially supported, writing this code is currently very complicated. But we don’t want to dive too deep into details; I just want to show the concept.

Once we implemented our HigherOrderComponent and the displayQueryParma function, we could use it in our routes.

Image for post
Image for post
usage of a function that returns a higher-order component in your routes definitions

This would render the HelloComponent and display the name property from the route. With the PriceComponent it would display the price property from the QueryParams.

Zone less change detection

However, Zone.js, has a couple of downsides. First, it's around 100 KB of size. This is a show stopper for Web components. Even though we can shrink our bundle to 10 KB size, you would still need to ship 100 KB of Zone.js for Change detection to work. The other big downside is that Zone.js can not patch native async/await statements.

How does Ivy help?

Well, Ivy introduces a markDirty function. The markDirty function takes a component as a reference. It then marks this component as dirty and causes change detection to run. The markDirty function also ensures that change detection doesn’t run too often.

Push pipe

{{ someStream$ | push }}

Reactive Components

Image for post
Image for post
Zone-less change detection with reactive components

Extending the ReactiveComponent would give us aconnect method to run Change Detection once our stream emits a value.

Conclusion

  • Smaller bundles out of the box
  • Lazy loading components
  • Improved debugging in the browser

Even more exciting than the features we can already use today is the view in the future. Ivy enables a lot of great things for the future of Angular. Some may make it to Angular, and some may not. It’s yet unclear. But Ivy offers the potential for the following features.

  • renderComponent — for tiny bundles
  • Tiny web components with the help of renderComponent
  • Optional NgModules
  • Higher-order components
  • Zone-less change detection

You can already play around with the thrill-seekers APIs. But don’t use them for production. We don’t know if those features will make it to Angular.

🧞‍ 🙏 If you liked this post, share it and give some claps👏🏻 by clicking multiple times on the clap button on the left side.

Feel free to check out some of my other articles about front-end development.

Google Developers Experts

Experts on various Google products talking tech.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store