Today I Learned

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. Adds active 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 with class.x syntax.

Understanding Angular Route Resolvers

Reference

  • 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 and ResolveEdn 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

Reference

  • 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

Reference

  • 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 as EventEmitter, 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 and setTimeout, and HTTP requests via XMLHttpRequest. There exists only one NgZone 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 an OnPush 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.
  • Event handler is triggered:
    • setTimeout, setInterval, Promise.resolve, this.http.get will not trigger CD using the OnPush CD strategy.
  • To trigger CD manually:
    • cdRef.detectChanges (can be with detach() 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 all OnPush ancestors to be checked for the next CD cycle.
  • Async pipe:
    • Recommended to use as much as possible so moving from Defaultto OnPush is a bit easier.
  • 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);
  }

This project is maintained by daryllxd