Santosh Yadav
Google Developer Expert for Angular GitHub Start and Nx Champion, Open source contributor for Angular and NgRx, creator ng deploy for Netlify, NestJSAddons core team.
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
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
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
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
export class ProductListComponent {
// throw error at compile time if product is not provided
@Input({ required: true }) products: any;
}
# PRESENTING CODE
export class ProductListComponent {
@Input() set status(status: boolean) {
this._status = status ? 'Active' : 'Inactive';
}
public get status () : string {
return this._status
}
}
# PRESENTING CODE
export class ProductListComponent {
@Input({ transform: (status: boolean) => (status ? 'Active' : 'Inactive') })
status: string = '';
}
# PRESENTING CODE
# 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
@Component({
selector: 'v-comment',
templateUrl: './comment.component.html',
styleUrls: ['./comment.component.scss'],
})
export class CommentComponent {
}
@NgModule({
declarations: [
CommentComponent
],
})
export class CommentModule { }
# PRESENTING CODE
@Component({
selector: 'v-comment',
standalone: true,
imports: [<dependent_module>, <dependent_component>]
templateUrl: './comment.component.html',
styleUrls: ['./comment.component.scss'],
})
export class CommentComponent {
}
# PRESENTING CODE
@Component({
imports: [<dependent_module>, <dependent_component>]
templateUrl: './comment.component.html',
styleUrls: ['./comment.component.scss'],
})
export class CommentComponent {
}
# PRESENTING CODE
Standalone APIs
@NgModule({
imports:
[
RouterModule.forRoot(routes),
HttpClientModule,
BrowserAnimationsModule
],
exports: [RouterModule],
})
export class AppRoutingModule {}
# PRESENTING CODE
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
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
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
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
Using Services
export class CommentComponent implements OnInit {
comments$ = this.commentService.getComments();
constructor(
private commentService: CommentService,
private activatedRoute: ActivatedRoute
) {}
}
# PRESENTING CODE
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
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
{
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
# PRESENTING CODE
@Component({
/* . . . */
})
export class ActivatedRouteComponent {
constructor(route: ActivatedRoute) {
@Input() user : string;
}
}
What's more
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser-esbuild",
}
# PRESENTING CODE
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
}
# PRESENTING CODE
"architect": {
"test": {
"builder": "@angular-devkit/build-angular:jest",
}
}
# PRESENTING CODE
<header-cmp />
@if showBody {
<body-cmp />
} @else if showSummary {
<summary-cmp />
} @else {
Nothing to see here...
}
<footer-cmp />
# PRESENTING CODE
@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
Thank you
@SantoshYadavDev
https://slides.com/santoshyadav/angular-past-present-future
By Santosh Yadav
Google Developer Expert for Angular GitHub Start and Nx Champion, Open source contributor for Angular and NgRx, creator ng deploy for Netlify, NestJSAddons core team.