Skip to content

Latest commit

 

History

History
543 lines (384 loc) · 37.5 KB

slides.md

File metadata and controls

543 lines (384 loc) · 37.5 KB
theme
white

XE ONE DAY - Modernize Angular App - Daniele Morosinotto

--

Un ringraziamento agli sponsor


UN PO' DI STORIA

INTRO

INTRO: I vari annunci e rilasci di NG:

  • 06 Feb 2020 - NG v9 inizio adozione Ivy - periodo stagnazione COVID + lavori interni ViewEngine->Ivy
  • 04 Nov 2021 - NG v13 Ivy everywhere (app+lib)
  • 06 Feb 2022 - RFC si inizia a parlare di Standalone
  • 02 Giu 2022 - NG v14 preview di Standalone comp
  • 16 Nov 2022 - NG v15 stable Standalone & API
  • 15 Feb 2023 - intro Alex a Signal + discussione RFC
  • 03 Mag 2023 - NG v16 preview di Signal, ma manca: signalComponent NG v17/18+ (zoneless in futuro...)

--

AGENDA

AGENDA

  • FEATURE PRINCIPALI:
    • Standalone Component -(SCAM)-> optional NgModule
    • Migrazione ng g @angular/core:standalone + qualche fix: AppComponent + SCAM exports
    • StandaloneAPI ottimizzazione (“tree-shaking”)
      • nuovo —> bootstrapApplication
      • provideHttp + withInterceptorXXX functional
      • provideRoute: nuovo Lazy loadComponent / loadChildren —> ROUTES
      • inizializzazione withXXX + functional Guard + uso dei providers su Route
    • inject function: dal ctor —> field + infer INJTOKEN + helpers injectParam(name) + attenzione al runInInjectorContext!
  • OPINIONI PERSONALI SU ALTRE FEATURES:
    • Signal: “the elefant in the room” cos’è come lo usiamo adesso e in futuro?!…
    • Accenni a directiveHosts / strict Typed ReactiveForms / img[ngSrc] & altre utili: @Input({required}) / withComponentInputBinding / DestroyRef / takeUntilDestoryed
  • EXTRA: Utilizzo di modern JS tools: esbuild + Vite | CONCLUSIONI: Q & A + Riferimenti

--

Le basi @NgModule

NgModule

  • Igor: “L’errore più grande del Team di Angular”
  • Ma a cosa serve l’@NgModule:
    • definizione del contesto di compilazione/scope del template: declarations, imports, exports
    • definizione dei providers per inizializzare la DI
    • entrypoint per Lazy load / creazione dinamica

DECISAMENTE UN PO’ TROPPO!

  • Tra l’altro il tutto con lo scopo di definire/usare i @Component che sono i veri building block dell'App

DEMO 01 sample NgModule

--

SCAM

SCAM

Soluzione tampone fino a NG13: Perchè io e altri è da un bel pò che vorremmo toglierci di torno gli @NgModule - alla fine ne facciamo centinaia :-/

SCAM = Single Component Angular ngModule

INLINE nello stesso file del @Component per facilitare il riuso+definizione del suo SCOPE template/compile!

DEMO 02 sample SCAM

--

Standalone @Component

standalone

@Component({
    selector: "app-some-cmp",
    template: `...`,
+   standalone: true,
+   imports: [OtherStandaloneCmp, LegacyModule, NgIf]
+   /* providers: [...] // EVENTUAL Services/provide rules for DI */
})
export class SomeComponent {...}
  • Pieno INTEROP con NgModule in entrambi i versi!
  • standalone @Directive/@Pipe -> Optional NgModule
    • più chiare dipendenze dirette (template scope)
    • più chiaro anche dove definire i providers (DI)
    • facilita creazione dinamica VCR.createComponet

COMPONENT PILAR: Finalmente i @Component sono i VERI BUILDING BLOCK delle Applicazioni Angular!


MIGRAZIONE standalone

--

MIGRAZIONE - CLI 15.2+

ng generate @angular/core:standalone
? Choose the type of migration: (Use arrow keys)
❯ Convert all components, directives and pipes to standalone
  Remove unnecessary NgModule classes
  Bootstrap the application using standalone APIs

Migrazione in 3 passi + richiede qualche FIX a mano:

  • SCAM spostare providers su comp prima del 2°
  • AppComponent riportare imports deps dopo 3°

DEMO 11 migrate 1°

DEMO 12 fix SCAM + 2°

DEMO 13 fix App.Comp + 3°

--

StandaloneAPI - provideHttp

  • La migrazione sistema bootstrapApplication + usa provideHttp ma NON ROUTING nuovo!?!
  • Alcune modifiche che possiamo fare:
    • cambiare AuthInterceptor riscrivendolo in modo funzionale + usando inject() per la DI!
    • utilizzare withInterceptors per caricare direttamente la HttpInterceptorFn in alternativa a withInterceptorsFromDi che usava la vecchia class.

DEMO 20 provideHttp + InterceptorFn


REFACTOR StandaloneAPI

--

provideRouter + fnGuard

  • Per migliorare "tree-shaking" possiamo usare provideRouter ### DEMO 30
  • Nuova sintassi Lazy loadChildren che punta direttamente a ROUTES ### DEMO 31
  • Possiamo definire providers direttamente nelle Route (elimino NgModule) ### DEMO 32
  • Possiamo caricare direttamente loadComponent -> Standalone Component ### DEMO 33 + Semplifico con export default ### DEMO 34
  • Semplifico scrittura RouteGuard canXYZ in modo funzionale! ### DEMO 35 ### DEMO 36

--

NEW DI - inject() DEMO 40

export class InAComponentOrService {
-   constructor(public someService: SomeService) {...} //BEFORE ctor
+   someService = inject(SomeService); //NOW you can use field=inject
}
  • Vantaggi: infer automatico del tipo di ritorno (utile per InjectionToken type-safe) ### DEMO 41
  • Semplifica caso di classi Ereditate NON ho più bisogno di ripassare dipendeze super(srvBase)
  • Possibile scrivere helper per riutilizzo logica injectFn vedi ### DEMO 42 - initFrm()
  • ma Attenzione a dove chiamiamo l'helper injectPar() può servire runInInjectionContext DEMO 43
  • Nuovi DI pattern esempio provideXXX+Config DEMO 44

Typed ReactiveForms DEMO 45

INTELLISENCE

SECONDO ME 👎 1) Sbagliato Naming! 2) Migra Untyped 3) troppo Incasinati gli oggetti/tipi strict che hanno scritto! 4) Troppo tardi! dopo 7 anni molti si son fatti altre soluzioni soprattutto per gestire Form dinamiche!

--

hostDirectives ###DOCS

  • Composition vs Inheritance per far mix&match di più direttive standalone rimappando @Input/@Output
@Directive({ standalone:true, ...})
export class Menu { }

@Directive({ standalone: true,...})
export class Tooltip { }

// MenuWithTooltip can compose behaviors from other directives
@Component({
  selector: 'menu-with-tooltip',
  template: './menu-with-tooltip.html',
  hostDirectives: [ Tooltip, {
    directive: Menu,
    inputs: ['menuId: id'],
    outputs: ['menuClosed: closed'],
  }],
})
export class MenuWithTooltipComponent { }

--

NgOptimizedImage ###DOCS

  • Utilizzo tag <img [ngSrc]=...> per migliorare LCP e gestire in modo ottimizzato il caricamento immagini (preload/lazy)
  • possibilità di impostare providers IMAGE_LOADER per utilizzare CDN (es: Cloudflare) o servizi di ContentManagment (es: Cloudinary)

LCP


MIGRARE A NG16

ng update @angular/cli@16 @angular/core@16

La migrazione a NG16 porta alcune novità utili:

  • runInInjectionContext fix injPar() DEMO 46
  • withComponentInputBinding per passare routerParams/Resolver -> @Input DEMO 47
  • @Input({required}) con assert automatico e controllo strict del template DEMO 48
  • DestoryRef per gestire dinamicamente OnDestroy -> utile per implementare tramite inject() il pattern takeUntilDestory DEMO 49

--

SIGNAL - "The 🐘 in the room"

E' una nuova PRIMITIVA REACTIVITY -> con lo scopo di migliorare/rendere la changeDetection puntuale!

const counter = signal(0); //infer WritableSignal<number>
const isOdd = computed(() => !!(counter() % 2)); //infer Signal<boolean>

counter.set(42); //RESETTA VALORE
counter.update((c) => c + 1); //AGGIORNA VALORE

effect(() => {
    //VIENE RICALCOLATO AUTOMATICAMENTE + NOTATE () PER LEGGERE VALORE
    console.log(`Adesso ${counter()} è ${isOdd() ? "dis" : ""}pari`);
});

DEMO 50 Signal base

--

Producer/Consumer + track deps & Notify

  • Praticamente è un contenitore di valori
  • che espone un getter () che ritorna in modo efficace "memoized" il valore corrente, ma internamente fa anche il tracciamento automatico di chi lo va ad utilizzare/leggere -> computed/effect
  • ha una serie di metodi per cambiare valore: set/update che scatenano Notifiche di cambiamento, e fanno partire "auto-ricalcolo Lazy" dei computed LIVE ed effect (push/pull + glitch-free)

DEMO 51 signal authToken + toObservable()

--

INTEROP con OBS$ DEMO 52

import { toObservable, toSignal } from "@angular/core/rxjs-interop";
  • toObservable(SIGNAL) -> OBS<T> Internamente usa un effect per reagire ai valori letti dal SIGNAL e pubblicarli sullo stream in uscita --> NECESSITA di esser chiamato in un InjectionContext o in alternativa passare option: {injector} per gestire il complete+destroy dell'effect quando termina il contesto
  • toSignal(OBS) -> SIGNAL<T|undefined> Internamente gestisce in automatico unsubscribe dell'Observable utilizzando DestroyRef (ricavato in automatico da InjectionContext o dall'{injector} passato)! All'inizio emette undefined perchè Observable è lazy ma signal è syncrono , ammeno che non si passi option {initialValue}

DEMO 52 toSignal() al posto di |async

--

Opinione un pò "contrastante":

PRO:

  1. Mi piace computed + richiamo () su template, senza problemi richiamare fn multipo/trick ng-container *ngIf as!
  2. In prima battuta potrebbe venire comodo per avere nuovo modo di evitare subscription |async usando toSignal(obs$) che gestisce in automatico unsubscribe
  3. Forse essendo una "primitiva reactivity" avremo i maggiori vantaggi nelle integrazioni di nuove lib per statemanagemnt -> ritorno Signal al posto di Obs$ vedi ngRxSignalStore

DEMO 53 exp dynamic computed

--

CONTRO: ### DEMO 54 exp dynamic effect

  1. NON è ancora chiarissimo come usarli / anti-pattern (vedi gestione async / effect con allowSignalWrites)
  2. I grossi vantaggi (CD granulare) arriveranno in FUTURO con Signal component NG 17-18+
  3. Richiede una riscrittura manuale +/- pesante del codice che attualmente è organizzato in base agli Observable RxJS...

Comunque sono sicuramente da tenere d'occhio in questi 1-2anni per capirli bene e prepararsi ad utilizzarli al meglio quando sarà completo il quadro!

--

TOFIX: TRACCIA SIGNAL

--

NOVITA NG16.X: nuova control-flow @syntax

MIGRAZIONE @syntax: ng generate @angular/core:control-flow

DEMO 61 uso di @if e @else

DEMO 62 uso di @for con track obbligatorio

DEMO 63 uso di @defer per rendere lazy parte del template con vari trigger

--

NOVITA NG17.X: nuove SingalIO api

MIGRAZIONE "NON UFFICILE" ng g ngxtension:convert-signal-inputs + ng g ngxtension:convert-outputs

DEMO 64 best-practices con effect

Esplicitare signal deps + untracked per evitare auto-track di parti non volute DEEP Articolo OZ su Graph

DEMO 65 uso di input(), input.required() e output()

DEMO 66 uso di input {trasfrom} + router withComponentInputBinding

DEMO 67 uso di model() + two-way binding e form


Extra: utilizzo modern JS tools

  • esbuild 🤯
  • Vite 💚 ⚡️
  • abilitato da standalone + inject

SU angular.json

"architect": {
    "build": {
-       "builder": "@angular-devkit/build-angular:browser",
+       "builder": "@angular-devkit/build-angular:browser-esbuild",
+       "builder": "@angular-devkit/build-angular:application", //for SSR

Le "mie" nuove Best Practices: standalone + inject + helper fn + pnpm + vite + esbuild


CONCLUSIONI + Q&A

  • Come abbiamo visto dopo un periodo stagnante, Angular negli ultimi 2anni ha avuto un grosso speedup nell'introduzione di nuove feature! Nuove funzionalità che lo hanno messo al passo con i tempi e che lo mantengono un framework moderno, forse un po' meno "opinionated" ma comunque hanno sempre posto la giusta attenzione alla retro compatibilità vedi quanto successo con Standalone!

--

  • Per il Futuro di Signal stanno seguendo stessi passi: introduzione dell'idea + discussione pubblica RFC e poi si va in preview e ci vogliono mediamente almeno 2 versioni per l'addozione... Quindi posso supporre che ci vorrano almeno fino a fine 2023 per una V17 in cui Signal sia più stabile e poi inizio/meta 2024 per una V18+ in cui si vedano i veri vantaggi di Signal, o forse anche di più!
  • Perchè come per IVY il passaggio "zone-less" con un nuovo sistema di ChangeDetection Granuale è sicuramente qualcosa di pesante cambiare e che impegnerà molto il Team di Angular per esser implementato soprattutto pensando alla retro compatibilità/coesistenza con l'esistente.

--

  • I vantaggi promessi sono interessanti e vedo fermento nella community per questo, anche se sicuramente richiederà anche da parte nostra degli sforzi/cambiamenti non banali per usarlo! Comunque l'unico modo che io conosco per ridurre l'impatto di un cambio così importante è quello di partire informati, c'è poco da fare il mondo non si ferma e noi dobbiamo tenerci al passo e cercare di anticipare per quanto possibile le direzioni e per farlo bisogna studiare e darci il tempo di capire come usarlo/sfruttarlo quando sarà disponibile! Così sapete cosa fare e non annoiarvi nei prossimi 2anni, o eventualemnte speare che ChatGPT10 faccia tutto per noi 🙃

FEEDBACK & CONTACT

Me

Daniele Morosinotto

Javascript enthusiast


REFERENCE LINKS

Standalone

Migrazione

  • Articolo su migrazione standalone su Angular 15 con schematics
  • Problemi migrazione EnterpriseApp -> Legacy comp/lib che non supportano Ivy ngcc rimosso da V16

Standalone API

  • Approfondimento sulle novità del provideHttp
  • Approfondimento sulle novità del provideRouter
  • Cambiamenti in Angular 14.2 Router Blog
  • Guida omni-compresiva su come fare Config dell'applicazione in Angular 15

Nuovo DI con inject

Altre features

Signal "Preview NG16"

NG V17+ NEWLINKS

SIGNAL NG17 NEWLINKS

STATEMANAGEMENT NGRX/SIGNAL

ALTRE LIB SIGNAL HELPERS

SIGNAL EXTRAS