TIL, 2021-09-07
exportAs
in the directive - means you can access the tooltip in the template.
@Directive({
selector: '[tooltip]',
exportAs: 'tooltip'
})
<a tooltip="I'm a tooltip!!" #tooltip="tooltip">I'm a link</a>
<button (click)="tooltip.toggleTooltip()">Toggle tooltip</button>
- This is some
ViewChild
kind of thing but with directives.
Reading Sample Angular app
routerLink
can have 2 arguments?routerLinkActive="active"
- Tracks whether the linked route of an element is currently active, and allows you to specify one or more CSS classes to add to the element when the linked route is active. Addsactive
CSS class if route matches correct place.- However, I’m thinking
[class.active]="routeMatches..."
is much more easier to reason about - at least we stay consistent withclass.x
syntax.
- However, I’m thinking
Understanding Angular Route Resolvers
- Acts like middleware, which can be executed before a component is loaded.
export interface Resolve<T> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T {
return 'Data resolved here...'
}
}
- Still not sure about this. On one hand it looks like it’s an SRP thing (where the responsibility of hydrating is in the resolver and not in the Angular view component. But on the other hand, you can’t do granular loading state per component and feels like they are harder to find. And you can do dispatch inside a smart component anyway.
- Router events:
NavigationStart
.RouterConfigLoadStart
: Route lazy loads a route configuration.RouteConfigLoadEnd
: After a route ha been lazy loaded.RoutesRecognized
: When the router parses the URL and the routes are recognized.GuardsCheckStart
: When the router begins guard.ChildActivationStart
: When the router begins activating a route’s children.ActivationStart
: When the router begins activating a route.GuardsCheckEnd
: When finished the guard phase successfully.ResolveStart
,ResolvedEnd
: Resolving.ChildActivationEnd
,ActivationEnd
NavigationEnd
NavigationCancel
NavigationError
Scroll
.
- You can add to
ResolveStart
andResolveEdn
to show or hide a loading animation to let the user know the data is being loaded. - Use resolver when you want to fetch the data even before the user is routed to the URL. This could include service calls which would bring us the data required to load the next page.
- If the resolver can like conditionally load, couldn’t we do that in the facade too?
- SRP - should the component manage the loading state of a fetch request? But we don’t do that, we manage that via reducer and effect.
A word on Angular route resolvers – and a praise for Reactive Programming
- The UX problem with resolvers:
- The router calls the resolver when the route is being requested, and then waits for the data to be resolved before the route is actually being activated. We want to show the page immediately after the link is clicked, but without the data.
- Solution: Don’t use resolvers. Just use Observables within components.
- Embrace functional and declarative programming, and practice with
Observable
and all the operators. Everything that happens somewhere can be interpreted as a stream of events or data. - Remember:
switchMap
cancels the running request in here:
ngOnInit() {
this.book$ = this.route.paramMap(
map(params => params.get('isbn')),
switchMap(isbn => this.bs.getOne(isbn))
);
}
- When to use resolver? None, apparently lol.
The Last Guide For Angular Change Detection You’ll Ever Need
- Change detection: The process of updating the view when the data has changed.
- How CD works:
- Developer updates the data model.
- Angular detects the change.
- CD checks EVERY component in the component tree to see if the corresponding model has changed.
- If there is a new value, it will update the component’s view (DOM).
- Zone.js
- A zone can keep track and intercept any asynchronous tasks.
- Angular patches several low-level browser APIs as startup to be able to detect changes in the application. This is done using
zone.js
which patches APIs such asEventEmitter
, DOM event listeners,XMLHttpRequest
,fs
API in Node. - The framework will trigger a change detection if one of the following events occurs: any browser event,
setInterval
andsetTimeout,
and HTTP requests viaXMLHttpRequest
. There exists only oneNgZone
and change detection is only triggered for async operations triggered in this zone.
- Change detection strategies:
Default
: Every component in the component tree is checked if an event triggers change detection.OnPush
: It only updates if:- The input reference has changed.
- CD is triggered manually.
- Component or one of its children triggers and event handler.
- Observable linked to the template via the async pipe emits a new value.
- Angular
Input
is passed by value for numbers, string, booleans, null, and undefined. For object and arrays, they are passed by reference - do not trigger CD on anOnPush
component. To trigger the CD, you need to pass a new object or array reference instead.- Note: This is why we do
cdRef
change check on header when user is logging in to change the navigation links - we didn’t return a new array/object. - We can prevent CD bugs, we can use immutable objects and lists.
- Note: This is why we do
- Event handler is triggered:
setTimeout
,setInterval
,Promise.resolve
,this.http.get
will not trigger CD using theOnPush
CD strategy.
- To trigger CD manually:
cdRef.detectChanges
(can be withdetach()
to implement local CD checks.ApplicationRef.tick
which triggers CD for the whole application by respecting the CD strategy of a component.cdRef.markForCheck()
which does not trigger CD but marks allOnPush
ancestors to be checked for the next CD cycle.
- Async pipe:
- Recommended to use as much as possible so moving from
Default
toOnPush
is a bit easier.
- Recommended to use as much as possible so moving from
ExpressionChangedAfterCheckedError
: It’s like you are changing something before the first change detection cycle to change that thing completed.- To not trigger change detection:
this.ngZOne.runOutsideAngular(() => {
});
- Used in E2E tests by protractor for
browser.waitForAngular
in the specs. - Deactivate (ex: if we use WebSocket to push a lot of data from the back-end to the front-end). This code triggers a change detection every 10 seconds.
constructor(private ref: ChangeDetectorRef) {
ref.detach(); // deactivate change detection
setInterval(() => {
this.ref.detectChanges(); // manually trigger change detection
}, 10 * 1000);
}