What's new in Angular 6.1?
Angular 6.1.0 is here!
keyvalue pipe
Angular 6.1 introduced a new pipe! It allows iterating over a Map or an object, and displaying the keys/values in our templates.
Note that it orders the keys:
- first lexicographically if they are both strings
- then by their value if they are both numbers
- then by their boolean value if they are both booleans (
false
beforetrue
).
And if the keys have different types, they will be cast to strings and then compared.
@Component({
selector: 'ns-ponies',
template: `
<ul>
<!-- entry contains { key: number, value: PonyModel } -->
<li *ngFor="let entry of ponies | keyvalue">
{{ entry.key }} - {{ entry.value.name }}
</li>
</ul>`
})
export class PoniesComponent {
ponies = new Map<number, PonyModel>();
constructor() {
this.ponies.set(103, { name: 'Rainbow Dash' });
this.ponies.set(56, { name: 'Pinkie Pie' });
}
}
If you have null
or undefined
keys,
they will be displayed at the end.
It’s also possible to define your own comparator function:
@Component({
selector: 'ns-ponies',
template: `
<ul>
<!-- entry contains { key: PonyModel, value: number } -->
<li *ngFor="let entry of poniesWithScore | keyvalue:ponyComparator">
{{ entry.key.name }} - {{ entry.value }}
</li>
</ul>`
})
export class PoniesComponent {
poniesWithScore = new Map<PonyModel, number>();
constructor() {
this.poniesWithScore.set({ name: 'Rainbow Dash' }, 430);
this.poniesWithScore.set({ name: 'Pinkie Pie' }, 125);
}
/*
* Defines a custom comparator to order the elements by the name of the PonyModel (the key)
*/
ponyComparator(a: KeyValue<PonyModel, number>, b: KeyValue<PonyModel, number>) {
if (a.key.name === b.key.name) {
return 0;
}
return a.key.name < b.key.name ? -1 : 1;
}
}
TypeScript 2.9 support
Angular 6.0 was stuck with TS 2.7, but Angular 6.1 catches up and adds support for TS 2.8 and 2.9.
You can check out what these new versions bring on the Microsoft blog:
Shadow DOM v1 support
As you may know, Angular offers an encapsulation
option
that allows to scope CSS styles to their component,
and their component only.
Until 6.1, Angular had three available options for this encapsulation option:
Emulated
, which is the default oneNative
, which relies on Shadow DOM v0None
, which means you don’t want encapsulation
Angular 6.1 introduces a new option: ShadowDom
, which relies on Shadow DOM v1,
the latest version of the specification.
Theoretically, it should be replacing the Native
option
(as the Shadow DOM v0 specification is now deprecated),
but it would be a breaking change,
so the team decided to introduce a brand new option.
If you’re into it, you can check out this awesome blog post listing the differences between Shadow DOM v0 and Shadow DOM v1. You can see the current support from the major browsers here. The support for Shadow DOM v1 will be better than for Shadow DOM v0 in the near future, as more browser vendors feel this is the right way to go.
Angular abstracts all the nitty gritty things to know about that, as you just have one option to switch to use Shadow DOM v1, and that’s pretty cool.
This new support also allows Angular Element to be used with the slot
elements
for basic native content projection.
Tree-shakeable services in core
You may remember that Angular 6.0 introduced tree-shakeable services,
with the possiblity to declare a service using @Injectable({ providedIn: 'root' })
.
The core services of the framework are starting to move to this new declaration,
with the first two services: Title
(which allows setting the title of the page)
and Meta
(which allows setting the metadata of the page).
It means that if you are not using them in your application, they will now not end up in your final bundle, saving a few bytes of JavaScript to send to our users.
Router scrolling position restoration
The router received some love in this release with the addition of a few features. The first one is an option allowing to restore the scrolling position when you navigate back to a component.
You simply have to add the option to your RouterModule
configuration:
imports: [
RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled'
})
]
Three differents values can be passed to this option:
disabled
, which does nothing (default).top
, which sets the scroll position to [0,0].enabled
, which sets the scroll position to the stored position.
The enabled
option will be the default in the future.
With this option, the router stores the scroll position when navigating forward,
and restores it when navigating back.
When navigating forward, the scroll position will be set to [0, 0], or to the anchor if one is provided.
It also adds an anchorScrolling
option,
to configure if the router should scroll to the element
when the url has a fragment.
It has two possible values:
disabled
, which does nothing (default).enabled
, which scrolls to the element. This option will be the default in the future.
And there is also a scrollOffset
option, if you want to add an offset to the scrolling.
It accepts a position, or a function returning a position.
The router now also emits a new event called Scroll
that you can listen to.
On paper, this looks super handy: if you have a very long template in a component, when a user navigates back to it, she will end up on her last scrolling position.
I say “on paper”, because in reality this only works with static content! If you have dynamic content displayed in the template (let’s say a very long list that you fetch from the server), the router will attempt to scroll even before the content is inserted… So it won’t scroll to the correct position, because this position will not exist when the router tries to scroll to it.
If you are in a case like this,
you’ll have to write tedious code to trigger the scroll yourself in the component,
by using a new service offered by the @angular/router
package,
called ViewportScroller
.
You could think that if the data are loaded via a resolver
,
the router would handle it correctly,
because the data are loaded before the component is displayed,
so it would make sense that the router would scroll to the right position in that case.
But sadly, currently, no… We opened an issue right away with this feedback (you can add a thumb up if you agree), but it is currently not adressed in 6.1.0.
So if you have dynamic content, you’ll have to handle the scroll yourself,
by writing tedious code looking like this, even if the data comes from a resolver
:
export class PendingRacesComponent {
scrollPosition: [number, number];
races: Array<RaceModel>;
constructor(route: ActivatedRoute, private router: Router, private viewportScroller: ViewportScroller) {
this.races = route.snapshot.data['races'];
this.router.events.pipe(
filter(e => e instanceof Scroll)
).subscribe(e => {
if ((e as Scroll).position) {
this.scrollPosition = (e as Scroll).position;
} else {
this.scrollPosition = [0, 0];
}
});
}
ngAfterViewInit() {
this.viewportScroller.scrollToPosition(this.scrollPosition);
}
}
And you’ll have to do the same in every component where you want the scroll position to be restored…
Router — URI error handler
You may have noticed that if a user tries to access a badly formed URL in your Angular application, the router will redirect to the root of the application.
Angular 6.1 introduces a new function called malformedUriErrorHandler
that you can provide to redirect your user to a different page.
imports: [
RouterTestingModule.forRoot(routes, {
malformedUriErrorHandler:
// redirects the user to `/invalid-uri`
(error: URIError, urlSerializer: UrlSerializer, url: string) => urlSerializer.parse('/invalid-uri')
})
]
As you can see, the handler receives the badly formed URL and the error, so you can even display a proper error to your users if you want.
Router — URL update strategy
In the same vein, if the router navigates to a component, and the navigation fails, the URL is currently not updated.
A new option urlUpdateStrategy
has been introduced,
and can receive either: deferred
or eager
.
deferred
is the default and only updates the URL if the navigation succeeds,
as it is the case currently.
eager
will start by updating the URL and then navigate to the component,
so the URL will be updated even if the navigation fails.
Angular CLI 6.1
The CLI has also been released in 6.1.0: check out our other article about what’s new!
All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!