The Framework of the Past, Present, and Future

Journey of Angular framework

About Me

Senior Software Enginner Celonis

GDE Angular 🅰️

GitHub Star ⭐

Nx Champion

Co-founder This is Learning

@SantoshYadavDev

# CHAPTER 2

Angular was the first framework with Typescript support

Schematics and Builders

A schematic is a template-based code generator that supports complex logic. It is a set of instructions for transforming a software project by generating or modifying code. Schematics are packaged into collections and installed with npm.

ng generate app <app_name>
  
ng generate lib <lib_name>
  
ng generate component <component_name>
# PRESENTING CODE

Schematics

A number of Angular CLI commands run a complex process on your code, such as linting, building, or testing. The commands use an internal tool called Architect to run CLI builders, which apply another tool to accomplish the wanted task.

"architect":{
   "build":{
      "builder":"@angular-devkit/build-angular:browser"
   },
   "serve":{
      "builder":"@angular-devkit/build-angular:dev-server"
   },
   "test":{
      "builder":"@angular-devkit/build-angular:karma"
   }
}
# PRESENTING CODE

Builders

Schematics and Builders API is available for developers and stable. This led to the inspiration for NxDevTools. 

ref: https://monorepo.tools/

Support for authoring library 

ng generate lib <lib_name>
# PRESENTING CODE

No setup needed

Can be published on npm

Uses Angular Package Format

Powerful @Input

Before

export class ProductListComponent {

  @Input() set products(product: any) {
    if(!product) {
      // thorw error if product is not provided at runtime
      throw new Error('Product is required'); 
    }
  }

}
# PRESENTING CODE

After

export class ProductListComponent {
  // throw error at compile time if product is not provided
  @Input({ required: true }) products: any;
}
# PRESENTING CODE

Before

export class ProductListComponent {

  @Input() set status(status: boolean) { 
    this._status = status ? 'Active' : 'Inactive';
  }
  
  public get status () : string {
    return this._status
  }
  

}
# PRESENTING CODE

After

export class ProductListComponent {
  
  @Input({ transform: (status: boolean) => (status ? 'Active' : 'Inactive') })
  status: string = '';

}
# PRESENTING CODE

After

# PRESENTING CODE
import {
  Component,
  Input,
  booleanAttribute,
  numberAttribute,
} from '@angular/core';

export class ProductListComponent {
  
  @Input({ transform: booleanAttribute })
  status: boolean = false;

  @Input({ transform: numberAttribute })
  count!: number;

}

Component Authoring

Before


@Component({
  selector: 'v-comment',
  templateUrl: './comment.component.html',
  styleUrls: ['./comment.component.scss'],
})
export class CommentComponent {
}

@NgModule({
  declarations: [
    CommentComponent
  ],
})
export class CommentModule { }

# PRESENTING CODE

After


@Component({
  selector: 'v-comment',
  standalone: true,
  imports: [<dependent_module>, <dependent_component>]
  templateUrl: './comment.component.html',
  styleUrls: ['./comment.component.scss'],
})
export class CommentComponent {
  
}
# PRESENTING CODE

Future


@Component({
  imports: [<dependent_module>, <dependent_component>]
  templateUrl: './comment.component.html',
  styleUrls: ['./comment.component.scss'],
})
export class CommentComponent {

}
# PRESENTING CODE

Standalone APIs

Before

@NgModule({
  imports: 
  [
    RouterModule.forRoot(routes), 
    HttpClientModule, 
    BrowserAnimationsModule
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}
# PRESENTING CODE

After

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient()/provideHttpClient(withFetch) -- in Developer Previee,
    provideAnimations(),
  ],
};

bootstrapApplication(AppComponent, appConfig).catch((err) =>
  console.error(err)
);
# PRESENTING CODE

Lazy loading 

Before

const routes: Routes = [
  {
    path: 'rooms',
    loadChildren: () =>
      import('./rooms/rooms.module').then((m) => m.RoomsModule),
    canActivate: [LoginGuard],
    canLoad: [LoginGuard], 
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
# PRESENTING CODE

After

const routes: Routes = [
  {
    path: 'rooms',
    loadComponent: () => import('./comment/comment.component').then((m) => m.CommentComponent),
    canActivate: [LoginGuard],
    canLoad: [LoginGuard], 
  }
];


export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
}


bootstrapApplication(AppComponent, appConfig).catch((err) =>
  console.error(err)
);
# PRESENTING CODE

Migrations

ng update
# PRESENTING CODE

Angular migrations

Image Optimization

@Component({
  standalone: true
  imports: [NgOptimizedImage],
})
class MyStandaloneComponent {}

providers: [
  provideImgixLoader("https://abc.com/"),
],

<img ngSrc="logo.png" width="200" height="100">
# PRESENTING CODE

Hydration


// Standalont App
export const appConfig: ApplicationConfig = {
  providers: [provideClientHydration()],
};


// With Module
@NgModule({
  providers: [provideClientHydration()],
})
export class AppModule {}
# PRESENTING CODE

Client Hydration

Using Services

Before

export class CommentComponent implements OnInit {
  comments$ = this.commentService.getComments();

  constructor(
    private commentService: CommentService,
    private activatedRoute: ActivatedRoute
  ) {}
  
}
# PRESENTING CODE

After

export class CommentComponent implements OnInit {
  comments$ = inject(CommentService).getComments();
 
  constructor(
      private activatedRoute: ActivatedRoute
  ) {}
  
}

# PRESENTING CODE

Change Detection & Signals

export const appConfig: ApplicationConfig = {
  providers: [
    provideExperimentalZonelessChangeDetection(),
    […]
  ]
}

Accessing Router Data

Before

import {ActivatedRoute} from '@angular/router';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
@Component({
  /* . . . */
})
export class ActivatedRouteComponent {
  constructor(route: ActivatedRoute) {
    const id: Observable<string> = route.params.pipe(map(p => p.id));
   
    const user = route.data.pipe(map(d => d.user));
  }
}
# PRESENTING CODE

After

 {
    path: 'product/:categoryName',
    loadComponent: () => import('@org/product').then((m) => m.ProductComponent),
    data: {
      user: 'Test User',
    },
  },

export const appConfig: ApplicationConfig = {
  providers: [  provideRouter(
    appRoutes,
    withComponentInputBinding())
    ]
}
# PRESENTING CODE

After

# PRESENTING CODE

@Component({
  /* . . . */
})
export class ActivatedRouteComponent {
  constructor(route: ActivatedRoute) {
    
    @Input() user : string;
    
  }
}

What's more

esbuild support

"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:browser-esbuild",
  }
# PRESENTING CODE

esbuild + vite builder support SSR, SSG

"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:application",
  }
# PRESENTING CODE

jest support for testing

"architect": {
  "test": {
          "builder": "@angular-devkit/build-angular:jest",
  }
  }
# PRESENTING CODE

Control flow

<header-cmp />
@if showBody {
  <body-cmp />
} @else if showSummary {
  <summary-cmp />
} @else {
  Nothing to see here...
}
<footer-cmp />
# PRESENTING CODE

Deferred loading

@defer on viewport {
  @main {
    <!-- this block will be deferred until the placeholder is visible -->
    <heavy-cmp />
  }
  @placeholder {
    <img src="ph.png">
  }
  @loading {
    <loading-spinner />  
  }
}
# PRESENTING CODE

Thank you for having me

My GitHub Sponosors

Free Angular Courses on YT

Thank you

@SantoshYadavDev

https://slides.com/santoshyadav/angular-past-present-future