Ivy improves the developer experience in many ways. In this section, we learn about the most noteworthy of them.
Improved compilation errors
Compile-time errors are a positive side effect of Angular using an AOT compiler. However, some of the build errors have needed additional context to aid in pinpointing the source of the error.
As an example, look at the error message in the following listing that is output when using an unknown element in an Angular application using View Engine:
ERROR in 'app-header' is not a known element:
1. If 'app-header' is an Angular component, then verify that it is part of this module.
2. If 'app-header' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<app-header></app-header>
<p>My content</p>
")
Which component has the error? How do we fix it?
The next listing shows the error message that is output for the same mistake in an Angular application using Ivy:
ERROR in src/app/app.component.html:1:1 - error NG8001: 'app-header' is not a known element:
1. If 'app-header' is an Angular component, then verify that it is part of this module.
2. If 'app-header' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
1 <app-header></app-header>
~~~~~~~~~~~~~~~~~~~~~~~~~
src/app/app.component.ts:5:16
5 templateUrl: './app.component.html',
~~~~~~~~~~~~~~~~~~~~~~
Error occurs in the template of component AppComponent.
The file path of the component model and the component template files are both listed, as well as the line number and the content of those lines. It is now much clearer where the problem was encountered—a big win for developer productivity.
Let's look at another example. If we accidentally add a duplicate comma to the imports
array of an Angular module, as seen in the following code block, a build error will be output:
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, , CommonModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
View Engine outputs an error message similar to the one shown in the following listing:
ERROR in src/app/app.module.ts(11,27): Error during template compile of 'AppModule'
Expression form not supported.
src/app/app.module.ts(11,27): Error during template compile of 'AppModule'
Expression form not supported.
Cannot determine the module for class AppComponent in C:/projects/sandbox/angular-cli8-app/src/app/app.component.ts! Add AppComponent to the NgModule to fix it.
We get the relevant file and line number, but the error description, Expression form not supported
, is not very helpful. Here is the equivalent error message output by Ivy:
ERROR in projects/second-app/src/app/app.module.ts:11:3 - error TS2322: Type '(typeof CommonModule | typeof BrowserModule | undefined)[]' is not assignable to type '(any[] | Type<any> | ModuleWithProviders<{}>)[]'.
Type 'typeof CommonModule | typeof BrowserModule | undefined' is not assignable to type 'any[] | Type<any> | ModuleWithProviders<{}>'.
Type 'undefined' is not assignable to type 'any[] | Type<any> | ModuleWithProviders<{}>'.
11 imports: [BrowserModule, , CommonModule],
~~~~~~~
In the previous listing, we immediately see that there is an issue in the imports
array and that somehow, an undefined
value ended up in it. Context is provided and the error description is improved.
Finally, let's look at the improved description of an error triggered by a piece of code that is not statically analyzable, as follows:
import { Component } from '@angular/core';
const template = location.href;
@Component({
selector: 'app-root',
styleUrls: ['./app.component.css'],
template,
})
export class AppComponent {}
The template
option in the previous example is not statically determinable as it relies on information only available at runtime. Here is the error message output by View Engine:
ERROR in No template specified for component AppComponent
In the previous listing, we see that the View Engine error message is for metadata that is not statically determinable. It tells us which component has an error, but it does not help us understand how to fix it. The type information shows that location.href
is of type string
, so why does AppComponent
not have a template
option according to the compiler?
The error message output by Ivy is a lot more helpful, as seen in the following listing:
ERROR in src/app/app.component.ts:8:3 - error NG1010: template must be a string
Value could not be determined statically.
8 template,
~~~~~~~~
src/app/app.component.ts:3:18
3 const template = location.href;
~~~~~~~~~~~~~
Unable to evaluate this expression statically.
node_modules/typescript/lib/lib.dom.d.ts:19441:13
19441 declare var location: Location;
~~~~~~~~~~~~~~~~~~
A value for 'location' cannot be determined statically, as it is an external declaration.
src/app/app.module.ts:8:5 - error NG6001: The class 'AppComponent' is listed in the declarations of the NgModule 'AppModule', but is not a directive, a component, or a pipe. Either remove it from the NgModule's declarations, or add an appropriate Angular decorator.
8 AppComponent
~~~~~~~~~~~~
src/app/app.component.ts:10:14
10 export class AppComponent { }
~~~~~~~~~~~~
'AppComponent' is declared here.
The error message output by Ivy doesn't just show us the line and expression that are not statically determinable. It goes one step further and shows us that the entire location
object is non-deterministic at compile time.
Read more about AOT limitations in Chapter 12, Embracing Ahead-of-Time Compilation.
Strict template type checking
View Engine had basic and full modes for template type checking. Ivy introduces strict template type checking. In addition to the full-mode template type checks, strict mode enables several checks, outlined as follows:
- Property binding types are checked against their corresponding input property type.
- Property binding type checks are strict about
null
and undefined
values.
- Generic types for components and directives are inferred and checked.
- Checks the type of template context variables, including
$implicit
.
- Checks the type of the
$event
template reference.
- Checks the type of template references to DOM elements.
- Safe navigation operations are type-checked.
- Array and object literals in component templates are type-checked.
- Attribute bindings are type-checked.
The strict check of null
for property bindings is important for property bindings using AsyncPipe
as it initially emits a null
value. This means that input properties being used with AsyncPipe
must either have a type that includes null
or use the concepts known as template guards and input setter type hints.
An even better update experience
As part of Ivy, the Angular command-line interface (CLI) adds three improvements to the update experience.
When running the ng update
command, the Angular CLI first downloads the latest stable version of the Angular CLI and uses it for the update to take advantage of the most recent improvements.
As part of the ng update
command, automated migrations are run by the Angular CLI. For every migration, a message is shown, such as the example shown in the following listing:
** Executing migrations of package '@angular/core' **
> Static flag migration.
Removes the `static` flag from dynamic queries.
As of Angular 9, the "static" flag defaults to false and is no longer required for your view and content queries.
Read more about this here: https://v9.angular.io/guide/migration-dynamic-flag
Migration completed.
Finally, the --create-commits
parameter flag is introduced to create a Git commit per migration to make it easier to debug the update process.
We will cover the Angular update process in more detail in Chapter 11, Migrating Your Angular Application from View Engine to Ivy.
Better IDE integration
Ivy introduces remarkable improvements to the Angular Language Service, which integrates with our IDE, such as VS Code.
Template and style Uniform Resource Locators (URLs) are verified inline, as seen in the following screenshot, where an error is displayed. This is especially helpful when renaming components:
Figure 2.1 – Invalid template URL error
Another neat improvement is that we can use the Go to definition IDE command to navigate to a component template or style sheet file. This makes it easier to work with one specific component.
Ivy versions of the Angular Language Service give us an additional context in tooltips for component models. The following screenshot shows a tooltip for a child component element in a component template. In the tooltip, we can see the Angular module that declared the child component. In the example, we see that ChildComponent
is declared by AppModule
:
Figure 2.2 – NgModule annotation in the component element tooltip
Similarly, the tooltip that appears for a component model also shows the Angular module that declared this component, as seen in the next screenshot. Here, we see that AppComponent
is declared by AppModule
:
Figure 2.3 – NgModule annotation in the component model tooltip
With Ivy, we can see method signatures in a tooltip, as seen in the following screenshot. This helps us reason about event handlers:
Figure 2.4 – Component method signature in the tooltip
Ivy's Angular Language Service enables us to see type annotations of UI properties and the $implicit
template reference. This allows us to get type information about an iterable UI property used with the NgFor
directive, as seen in the following screenshot:
Figure 2.5 – Iterable UI property tooltip
The next screenshot shows how Ivy enables us to get type information about the named iterator template reference in each loop cycle:
Figure 2.6 – Named iterator template reference tooltip
Combined with strict template type checking, as described earlier in this chapter, this allows us to catch type errors in components early and consistently.
The improved Angular Language Service released with Ivy adds syntax highlighting to inline component templates and styles, as seen in the following screenshot. This makes single-file components easier to use:
Figure 2.7 – Syntax highlighting for inline component template and styles
Inline templates even benefit from the ability to add syntax highlighting to template expressions also introduced with Ivy.
Finally, Ivy adds support for style preprocessors such as Sass in inline styles by introducing the inlineStyleLanguage
option to the Angular @angular-devkit/build-angular:browser
and @angular-devkit/build-angular:karma
builders, for example: "inlineStyleLanguage":
"scss"
.