Angular’s New Era: Unveiling the Most Exciting Features Yet

  • 🧑‍💻 Simple Angular app
  • 🧱 Standalone component
  • 🎮 Control flow (if, for, switch, & defer)
  • 🔄 Routes 🛤️
  • 💦 Incremental Hydration
  • 🚦 Signals
  • 🌍 Http Call using Resource API
  • 🚀 Runtime performance boost 🏎️💨
  • 🚧 Upcoming features

Agenda

 🧑‍💻Simple Angular App

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    Hello from {{ name }}!
  `,
})
export class App {
  name = 'Angular';
}

bootstrapApplication(App);
<app-root></app-root>

📦 Framework bundle sizes (gzip)

Pankaj P. Parkar

Principal Application Devloper @AON

  • Angular GDE

  • Ex- Microsoft MVP (2015-22)

  • @ngx-lib/multiselect 📦 

About Me!

@pankajparkar

#devwhorun 🏃‍♂️ 

🧱 Standalone Component

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    Navbar,
    UserProfile,
  ],
  template: `
    <navbar />
    <user-profile />
  `,
})
export class App { }
@Component({
  selector: 'user-profile',
  standalone: true,
  template: `
    <h1>User Profile</h1>
    User details here...
  `,
})
export class UserProfile { }
@Component({
  selector: 'navbar',
  standalone: true,
  template: `
    Navbar at the top
  `,
})
export class Navbar { }
<app-root></app-root>
@Component({
  selector: 'user-profile',
  imports: [
	NgIf,
    UserDetails,
    NoDataFound,
  ],
  template: `
    <h1>User Profile</h1>

    <div *ngIf="user; else #noDataFound">
      <user-details
        [user]="user">
      </user-details>
    </div>

    <ng-template #noDataFound>
      <no-data-found></no-data-found>
    </ng-template>
  `,
})
export class UserProfile { }

🔀 Conditional Rendering - if

@Component({
  selector: 'user-profile',
  imports: [
    UserDetails,
    NoDataFound,
  ],
  template: `
    <h1>User Profile</h1>
	@if (user) {
      <user-details
		[user]="user" />
    } @else {
	  <no-data-found />
    }
  `,
})
export class UserProfile { }

🔂 Control flow - For

Name: {{user.name}}
<ul>
  @for(hb of user.hobbies; track hb.id) {
    <li> {{ hb.name }} </li>
  }
  @empty {
    <li>
      <no-data-found />
	</li>
  }
</ul>
Name: {{user.name}}
<ul>
  <li *ngFor="let hb of user.hobbies">
	{{ hb.name }}
  </li>
  <li *ngIf="!user.hobbies.length">
	<no-data-found />
  </li>
</ul>

🔌 Control flow - switch 🔌

<div [ngSwitch]="accessLevel">
  <admin-dashboard *ngSwitchCase="admin"/>
  <moderator-dashboard *ngSwitchCase="moderator"/>
  <user-dashboard *ngSwitchDefault/>
</div>
@switch (accessLevel) {
  @case ('admin') {
    <admin-dashboard/>
  }
  @case ('moderator') { 
    <moderator-dashboard/> 
  }
  @default { 
    <user-dashboard/> 
  }
}

🥱 Deferred view load

@defer (on viewport) {
  <comment-list/>
}
@loading (minimum 1000ms) {
  Loading ⌛️
}
@error {
  Loading failed 🙁
}
@placeholder {
  <img src="comments-placeholder.png">
}

🥱 Deferred view load

goo.gle/js-benchmarks

🔄 Routes 🛤️

export const routes: Routes = [
  {
    path: 'home',
    component: Home,
  },
  {
    path: 'user',
    children: [
	  {
		path: 'edit/:id',
		component: UserEdit,
	  },
	  {
		path: 'add',
		component: UserEdit,
	  },
	  {
		path: 'list',
		component: UserList,
	  },
    ]
  },
  {
    path: '**',
    redirectTo: 'home',
  },
];
// main.ts
bootstrapApplication(AppComponent, {
  providers: [
    provideRoute(routes),
  ],
});

/home

/user

📑

📑

🌎

https://abc.com

Routes - lazy loading.. 💤

export const routes: Routes = [
  {
    path: 'home',
    component: Home,
  },
  {
    path: 'user',
    loadChildren: () => import('./user/routes')
  },
  {
    path: '**',
    redirectTo: 'home',
  },
];
// user/routes.ts
export default [
  {
    path: 'edit/:id',
    loadComponent: () => import('./user-edit.component'),
  },
  {
    path: 'add',
    loadComponent: () => import('./user-add.component'),
  },
  {
    path: 'list',
    loadComponent: () => import('./user-list.component'),
  },
];

Hydration 💧

Incremental Hydration 💦

⏯️ Event Replay 

import {
  provideClientHydration,
  withEventReplay,
  withIncrementalHydration,
} from '@angular/platform-browser';

bootstrapApplication(App, {
  providers: [
    providerRouter(routes),
    ...,
    provideClientHydration(
	  withEventReplay(),
	  withIncrementalHydration(),
  	)
  ]
});
@defer (on idle; hydrate on interaction) {
  <example-cmp />
} @placeholder{
  <div>Example Placeholder</div>
}

 💦 Incremental Hydration + Event Replay ⏯️

🚦 Signals

What is Reactivity?

Change propagation through dependencies tracking

Custom implementation

FirstName LastName FullName
Adam Clarke Adam Clarke
Phil Smith Phil Smith
firstName = 'Adam';

lastName = 'Clarke';

fullName = `${this.firstName} ${this.lastName}`;
console.log(this.fullName); // Adam Clarke

updateFirstName() {
  this.firstName = 'Mr. Adam';
}

this.updateFirstName();
console.log(this.fullName); // Adam Clarke
get fullName() {
  return `${this.firstName} ${this.lastName}`;
}

🚦Angular Signal🚦

  • helps to update application state in optimised way
  • implementation is based on reactive principals
  • callable function 
const count = signal(1);
const age = signal<number>(18);
const user = signal<User>({
  id: 1,
  name: 'Pankaj'
});

Signal implementation

@Component({
  ...,
})
export class SignalComponent {
  firstName = signal('Pankaj'); // declare
  lastName = signal('Parkar'); // declare
  
  // derive a value from other signal
  fullName = computed(() => 
    // get signal value by calling `()`
    `${this.firstName()} ${this.lastName()}`
  );

  firstNameEffect = effect(() => 
    console.log(this.firstName())
  );

  updateFirstName() {
    // set signal value
    this.firstName.set('Pankajj');
  }
}
<div>
  {{firstName()}}
  {{lastName()}}
  {{fullName()}}
</div>

<button
  (click)="updateFirstName()">
  Update FirstName
</button>

Reference: Stackblitz

🧪 Resource for HTTP 

@for(product of products.value(); track product.id){
  <tr>
    <td>{{p.id}}</td>
    <td>{{p.name}}</td>
    <td>{{p.price}}</td>
  </tr>
}
@if(product.error()) {
  Error retrieving data... 🚨📢🔔⚠️
}
@if(product.isLoading()) {
  Loading ... ⌛
}
product = resource({
  loader: async (param) => {
    return fetch('http://localhost:3000/products')
	  .then((res) => res.json() as Promise<Product[]>);
  },
});

,🚀 Performance boost 🏎️💨

quantity.set(2)

quantity()

,🚀 Performance boost 🏎️💨

quantity.set(2)

quantity()

quantity()

Upcoming features

  • Zoneless Change Detection
  • Incremental hydration
  • Resource API
  • linkedSignal API
  • Route-level render mode
  • Signal forms

References

  • https://prateeksurana.me/blog/future-of-rendering-in-react/
  • https://blog.angular.dev/angular-v16-is-here-4d7a28ec680d
  • https://blog.angular.dev/introducing-angular-v17-4d7033312e4b
  • https://blog.angular.dev/angular-v18-is-now-available-e79d5ac0affe
  • https://blog.angular.dev/meet-angular-v19-7b29dfd05b84
  • https://www.telerik.com/blogs/getting-started-resource-api-angular
  • https://krausest.github.io/js-framework-benchmark/current.html

@pankajparkar

https://pankajparkar.dev

Made with Slides.com