# Angular 16 `tech sharing` ## Contents - Signal - Others - Hooks - Hydration - Development tool ## Feature ### Signal #### Background - using zone berfore Change detection method is to "detect changes when asynchronous events occur" (these asynchronous examples are Http Request, setTimeout, etc.), and Angular wraps the zone.js program so that we can get it through it notify the occurrence of all events and trigger change detection. #### What's the problem ? - Unnecessary trigger #### "Signal" for better performace - Better runtime performance - Enables more prescriptive interactivity - in a future version will allow us to make changes only in component inspections - Better interoperability with RxJS ex. There are toObservable , toSignal etc. available - Make Zone.js optional #### How it works ? - Overall workflow: ![](https://hackmd.io/_uploads/H1XOPK5_n.png) 1. Set value in signal and the consumer vill be notified. 2. Consumer will read the value in signal. #### Function - `signal()` ![](https://hackmd.io/_uploads/rJTMsOJfp.png) - What it does: create signal , return `WritableSignal<T>.Signal` (it's a getter function, but the type WritableSignal give us the possibilty to modifiy the value by three methods.) ```typescript= import { signal } from '@angular/core'; // create signal targetSignal = signal(0); ``` - Detail function: - `set[set(value: T): void]` set the signal to a new value, and notify any dependents ```typescript= targetSignal.set({name: 'Clemmy', age: INFINITE}) ``` - `update[update(updateFn: (value: T) => T)]` update the value of the signal based on its current value, and notify any dependents ```typescript= targetSignal.update(number => number + 1); ``` - `mutate[mutate(mutateFn: (value: T) => void)]` update the current value by mutating it in-place, and notify any dependents. ```typescript= // 更改 list value targetSignal.mutate(test_list => {test_list[0].done = true;}); ``` Then we use the flollowing function to create consumer: ![](https://hackmd.io/_uploads/BySe49_hn.png) - `computed(): Signal<T>` - What it does ? create a "new signal" that automatically updates whenever its dependencies change -> computed function will return another Signal, any signals used by computation will be tracked as dependencies, and the value of the new signal recalculated whenever any of those dependencies changes. ```typescript= targetComputedSignal = computed(() => return targetSignal.length); ``` - Workflow: ![](https://hackmd.io/_uploads/BkRI69Onh.png) - Note: computed function returns a "non" WritableSignal , so we can't manually modify the returned signal by methods such as `set`, `update`, or `mutate`. (check "give it a try" removeTeam function: https://stackblitz.com/edit/stackblitz-starters-e8u6wu?file=src%2Fmain.ts) - `effect(): EffectRef ` - What it does? when we want to use some side effect related implementation, we can use effect function to do this. ```typescript= effect(() => console.log(`${targetSignal().length}`)); ``` - Clean up (add in effect function) This function allows you to register a callback that will be invoked before the next run of the effect begins or when the effect is destroyed ```typescript= private loggingEffect = effect((onCleanup) => { // it is onCleanup(() => { console.log(`Cleaned up!`); }); }); ``` - Automatically destroy When you create an effect, it is automatically destroyed when its enclosing context is destroyed. - If you want to destroy effect manually - set manualCleanup and call destroy ```typescript= // effect function returns EffectRef demoDestroyEffect = effect( () => { console.log('test for effect'); }, { manualCleanup: true } ); // we can use destroy to destroy the effect demoDestroyEffect.destroy(); ``` They work like this: ![](https://hackmd.io/_uploads/r1Ha39un2.png) #### Give it a try: https://stackblitz.com/edit/stackblitz-starters-e8u6wu?file=src%2Fmain.ts ### Others #### New hooks - Reference: https://angular.io/guide/lifecycle-hooks#reading-and-writing-the-dom - Hooks - `afterNextRender`: Once after the next change detection cycle. - Purpose: Perform one-time initialization, or observe a single, specific change to the DOM. - Example: ```typescript= @Component({ selector: 'my-cmp', template: `<span #content>{{ ... }}</span>`, }) export class MyComponent { resizeObserver: ResizeObserver|null = null; @ViewChild('content') contentRef: ElementRef; constructor() { afterNextRender(() => { this.resizeObserver = new ResizeObserver(() => { console.log('Content was resized'); }); this.resizeObserver.observe(this.contentRef.nativeElement); }); } ngOnDestroy() { this.resizeObserver?.disconnect(); this.resizeObserver = null; } } ``` - `afterRender`: After every change detection cycle that follows. - Purpose: Synchronize state with the DOM. ```typescript= @Component({ selector: 'my-cmp', template: `<span #content>{{ ... }}</span>`, }) export class MyComponent { @ViewChild('content') contentRef: ElementRef; constructor() { afterRender(() => { const elem = this.contentRef.nativeElement; console.log(`content position: (${elem.offsetLeft}, ${elem.offsetTop})`); }); } } ``` - Why they provide these new hook ? If we want to read or write the DOM, we can use the old lifecycle function. However, if we use those lifecycles, **its code may be triggered during server-side rendering and pre-rendering.** That's why we need `afterNextRender` & `afterRender`. #### SSR (server side render) related - Hydration (better hydration: non-destructive hydration feature) 1. What is hydration? A process in which the HTML generated on the server side is added with an event handler (event handler) through JavaScript on the client side to give it interactive capabilities. 2. Angular 16 - Better hydration Hydration improves application performance by **avoiding extra work to re-create DOM nodes**. (without the need to destroy and rerender them entirely.) ![](https://hackmd.io/_uploads/H1arZqyfa.png) 2. Hydration could improve performance through: - allows the existing DOM to be re-used - prevents a flicker - Original problem: This flickering issue arises from Angular’s process of destroying and reloading DOM structures upon re-bootstrapping on the client machine. 3. How to use hydration ? - Check if you have SSR working with your application - Enable hydration by visiting your main app component or module and ```importing provideClientHydration from @angular/platform-browser``` ```typescript= import { bootstrapApplication, provideClientHydration, } from '@angular/platform-browser'; ... bootstrapApplication(RootCmp, { // add line below providers: [provideClientHydration()] }); ``` Or if you are using NgModules, you would add provideClientHydration to your root app module's provider list. ```typescript= import {provideClientHydration} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; @NgModule({ declarations: [RootCmp], exports: [RootCmp], bootstrap: [RootCmp], // add line below providers: [provideClientHydration()], }) export class AppModule {} ``` - Notes: We will likely need to **fix instances of Direct DOM Manipulation before hydration** will fully work either by switching to Angular constructs or by using ```ngSkipHydration```. - What is Direct DOM Manipulation? Example: - Manipulate DOM using DOM API like: `innerHTML`, `outerHTML` - Accessing the `document` - Injecting additional nodes using `appendChild` - Detach DOM nodes - Why we need to do so ? > Because Angular is unaware of these DOM changes and cannot resolve them during the hydration process. - How to skip hydration for particular components ? html - add ngSkipHydration in tag ```html= <example-cmp ngSkipHydration /> ``` ts - add host: {ngSkipHydration: 'true'} ```typescript= @Component({ ... host: {ngSkipHydration: 'true'}, }) class ExampleCmp {} ``` ### Development tool - New build tool New build tool can help us reduce build times ![](https://hackmd.io/_uploads/B1Toyysa2.png) - Standalone migration - Standalone migration guide https://angular.io/guide/standalone-migration - Steps 1. Run this command in terminal: ``` ng g @angular/core:standalone ``` 2. Run the command and select the option respectively - Run ```ng g @angular/core:standalone``` and select "Convert all components, directives and pipes to standalone" - Run ```ng g @angular/core:standalone``` and select "Remove unnecessary NgModule classes" - Run ```ng g @angular/core:standalone``` and select "Bootstrap the project using standalone APIs" - Run any linting and formatting checks, fix any failures, and commit the result (bc we still need to fix errors) --- ## Reference - Feature Highlights: https://angular.io/guide/update-to-version-16 - Angular 16: https://blog.angular.io/angular-v16-is-here-4d7a28ec680d - Signal-related: https://infinum.com/blog/introducing-angular-signals/ - Signals-related: https://itnext.io/angular-signals-the-future-of-angular-395a69e60062 - Hydration: https://betterprogramming.pub/unveiling-angular-hydration-revolutionizing-rendering-for-enhanced-web-experiences-98c12b31f464 - Solid: https://github.com/solidjs/solid - signal and rxjs: https://levelup.gitconnected.com/signals-in-angular-is-rxjs-doomed-5b5dac574306