The new version, v10, of Angular has been published only hours ago and announced by this blog post. Although it may not appear as impactful as v9 (with Ivy and all), this release displays Angular team's commitment to keep Angular up-to-date and relevant. We found this very exciting and the timing was just right, because we are about to release ABP v3.0!
So, we jumped into the details of what changed and how to migrate. Here is what we found.
Major Changes
TypeScript v3.9 Support [breaking change]
Angular 9 was released with TypeScript 3.7 support. Soon TypeScript 3.8 was released and Angular v9.1 supported it. Not long after, another TypeScript version, 3.9, is released and Angular responds with v10, keeping up, not only with TypeScript, but also with TSLib and TSLint.
That is great news. Angular stays up-to-date. First of all, TypeScript 3.9 has performance improvements, which means faster Angular builds, especially in larger projects. Second, all latest TypeScript fixes and features are available to Angular developers. Last, but not the least, Angular developers will be using a more elaborate TypeScript configuration.
Earlier versions of TypeScript are no longer supported, so you have to install v3.9 in your project. I believe a major reason behind this is the next feature I will describe.
"Solution Style” tsconfig.json
Files
"Solution Style” tsconfig.json
file support was introduced by TypeScript v3.9 to overcome issues with cases where a tsconfig.json existed just to reference other tsconfig.json files, known as a "solution". Angular 10 makes use of that concept and improves IDE support and, consequently, developer experience.
A new file called tsconfig.base.json
is introduced and what was inside the root tsconfig.json
before is carried to this new file. You can find further details about the "solution" configuration here, but basically the new tsconfig.json
at root level, before and after adding a library to the project, looks like this:
BEFORE:
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./e2e/tsconfig.json"
}
]
}
AFTER:
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./e2e/tsconfig.json"
},
{
"path": "./projects/core/tsconfig.lib.json"
},
{
"path": "./projects/core/tsconfig.spec.json"
}
]
}
If you upgrade to Angular 10 using ng update
, the CLI will migrate your workspace to this structure. Earlier versions of TypeScript does not support "solution style", so this may be the reason behind the breaking change described above.
Angular Package Format Changes & esm5/fesm5
Angular package format has changed, and the new format does not include esm5
and fesm5
distributions anymore. Angular packages (@angular/*) will not include them. Since Angular generates ES5 from ES2015 and ES2015 is the default language level consumed by Angular tooling, those code distributions had become obsolete. The format change is as follows:
BEFORE:
{
...
"main": "bundles/abp-ng.core.umd.js",
"module": "fesm5/abp-ng.core.js",
"es2015": "fesm2015/abp-ng.core.js",
"esm5": "esm5/abp-ng.core.js",
"esm2015": "esm2015/abp-ng.core.js",
"fesm5": "fesm5/abp-ng.core.js",
"fesm2015": "fesm2015/abp-ng.core.js",
...
}
AFTER:
{
...
"main": "bundles/abp-ng.core.umd.js",
"module": "fesm2015/abp-ng.core.js",
"es2015": "fesm2015/abp-ng.core.js",
"esm2015": "esm2015/abp-ng.core.js",
"fesm2015": "fesm2015/abp-ng.core.js",
...
}
If your application depends on esm5/fesm5 files, you can relax, because they are still consumable by the build system. Likewise, you do not have to worry if your Angular library does not ship esm2015
or fesm2015
, because the CLI will fallback to others. However, in favor of bundle optimization and build speed, it is recommended to deliver ES2015 outputs.
Browserlist
Angular generates bundles based on the Browserlist configuration provided in the root app folder. Angular 10 will look up for a .browserlistrc
in your app, but fall back to browserlist
if not found. The ng update
command will rename the file for you.
Breaking Changes
- Resolvers that return
EMPTY
will cancel navigation. In order to allow the router to continue navigating to the route, emit some value such asof(null)
. - Logging of unknown property bindings or element names in templates is increased to "error" level. It was "warning" before. The change may have an effect on tools not expecting an error log.
- Returning
null
from a UrlMatcher would throwType 'null' is not assignable to type 'UrlMatchResult'.
before. This is fixed, but the return type can now benull
too. - Reactive forms had a bug which caused
valueChanges
for controls bound to input fields withnumber
type to fire twice. The listened evet is changed from "change" to "input" to fix this. minLength
andmaxLength
validators validate only if the value has a numericlength
property.- There was a bug in detection of day span while using
formatDate()
orDatePipe
andb
orB
format code. It is fixed and the output, for instance, will now be "at night" instead of "AM". - Transplanted views (declared in one component and inserted into another) had change detection issues, but that is fixed now. Detection after detaching and double detection are avoided.
Deprecations and Removals
ModuleWithProviders Without a Generic Type [removed]
Earlier versions of Angular was able to compile static method returns with ModuleWithProviders
type without the generic type, i.e. ModuleWithProviders<SomeModule>
, because the generated metadata.json
files would have the information required for compilation. After Ivy, since metadata.json
is not required, Angular checks the generic type for type validation.
BEFORE:
@NgModule({...})
export class SomeModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: SomeModule,
providers: [...]
};
}
}
AFTER:
@NgModule({...})
export class SomeModule {
static forRoot(): ModuleWithProviders<SomeModule> {
return {
ngModule: SomeModule,
providers: [...]
};
}
}
ModuleWithProviders
without a generic type was deprecated before. As of Angular 10, it is completely removed.
Undecorated Base Classes [removed]
If you are taking advantage of inheritance from classes that use Angular features such as dependency injection or Angular decorators , you now need to decorate those base classes as well. Otherwise, Angular will throw an error about the missing decorator on the parent.
DEPENDENCY INJECTION:
@Directive()
export abstract class AbstractSome {
constructor(@Inject(LOCALE_ID) public locale: string) {}
}
@Component({
selector: 'app-some',
template: 'Locale: {{ locale }}',
})
export class SomeComponent extends AbstractSome {}
Here is the error Angular 10 compiler throws when the Directive
decorator is missing:
The component SomeComponent inherits its constructor from AbstractSome, but the latter does not have an Angular decorator of its own. Dependency injection will not be able to resolve the parameters of AbstractSome's constructor. Either add a @Directive decorator to AbstractSome, or add an explicit constructor to SomeComponent.
DECORATOR:
@Directive()
export abstract class AbstractSome {
@Input() x: number;
}
@Component({
selector: 'app-some',
template: 'Value of X: {{ x }}',
})
export class SomeComponent extends AbstractSome {}
Angular 10 compiler throws a less detailed error this time:
Class is using Angular features but is not decorated. Please add an explicit Angular decorator.
I am sure you would not do that, but if you put a Component
decorator on the parent and remove the decorator on the child, as you would expect, Angular 10 compiler will throw the error below:
The class 'SomeComponent' 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.
WrappedValue [deprecated]
WrappedValue
is deprecated and will probably be removed with v12. Check here and here if you have never heard of it before.
It was useful to trigger change detection even when same object instance was produced/emitted. There is a performance cost when WrappedValue
is used and the cases where it helps are relatively rare, so Angular team has decided to drop it.
As a side effect of this deprecation, you may see more ExpressionChangedAfterItHasBeenChecked
errors than before, because Angular would not throw an error when WrappedValue
s were evaluated as equal.
Incase you face change detection issues, try cloning the object or trigger change detection manually via markForCheck
or detectChanges
methods of the ChangeDetectorRef
.
Other Deprecations & Removals
- Support for IE9, IE10, and IE Mobile has been deprecated and will be dropped later. The increased bundle size and complexity was the main reason. Considering even Microsoft dropped support for these browsers, it makes a lot of sense.
- Angular stopped sanitizing the style property bindings. This is due to drop of support for legacy browsers (like IE6 and IE7) and the performance cost of having a sanitizer.
- Bazel build schematics will not be continued. Angular team explained the reasons and referred to this monorepo as a source to keep an eye on, if you are interested in building Angular with Bazel.
Conclusion
I would like to emphasize how thankful I am that Angular team is trying to keep Angular up-to-date. This is a great demonstration and, in my humble opinion, just as meaningful as a state-of-art renderer. It is also very nice to see how easy it is to migrate an existing project. Angular not only keeps up with its ecosystem, but also helps you to keep up together with it.
Congratulations, and thank you!