State of

New features and Dig into Standalone

Components

In a nutshell

Communities

Social Developer

Recognitions

Product Manager @Microsoft

First GDE,MCT and MVP from SL

Top stackoverflow contributor

 

I'm Sajeetharan Sinnathurai

@sajeetharan
@kokkisajee
@sajeetharan
@sajeetharan

Let's start with a Demo

"HEROES DON'T ALWAYS WEAR CAPES

SOMETIMES THEY BUILD ANGULAR APPS"

  • A framework for web front-end app 

  • A platform for integrated development

  • An ecosystem for developers

State of JS / USAGE

Community -> Angular

Feedback

Core dev

Ecosystem

Submit a   feature   request

5.5K -> 0.7K

open issues

Angular (v2)

2016

Future ready

$scope

ng-if

ng-app

ng-model

mobile

oriented

better

performance

x11

Angular 1.x vs Angular

Stable and performant

Language service && Tooling

Meet Developers where they are

Smaller, faster, easier

Angular (v4)    2017

Angular (v10)  2020

Angular (v12)  2021

Angular (v8)    2019

Angular (v8)    2019

Ivy Everywhere

Angular (v15)  2022

New Features (v14/15)

Image Optimized Directive

Inject API

Developer diagnostics

/ CLI improvements

(ng completion/ ng cache)

Standalone Components

Page Title Strategy

Strictly Typed Forms

!

kokkisajee

Strictly Typed Form

 userForm = new FormGroup({
   name: new FormControl(''),
   age: new FormControl(''),
   address: new FormGroup({
      city: new FormControl(),
      state: new FormControl(),
      postalCode: new FormControl(),
   }),
 });
userForm = new FormGroup({
  name: new FormControl<string>(''),
  age: new FormControl<number | null>(null),
  address: new FormGroup({
    city: new FormControl<string>(''),
    state: new FormControl<string>(''),
    postalCode: new FormControl<number | null>(null),
  }),
});

Page Title Strategy - a11y

Route Level (hard coded)

const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    title: 'Dashboard',
  },
  {
    path: 'typed-forms',
    component: TypedFormsComponent,
    title: 'Typed Forms'
  },
  {
    path: 'standalone-component',
    component: StandaloneComponent,
    title: 'Standalone Component',
  },
  ...
  
 ];

kokkisajee

Inject() 💉

Reference: https://nartc.me/blog/inheritance-angular-inject

It takes an instance of dependency from the current injector tree

@Component(...)
abstract class TestComponent {
  constructor(
    private a: A
  ) { }
}
@Component(...)
abstract class TestComponent {
  a = Inject(A); // <- ✅
}
@Component(...)
abstract class TestComponent {
  a: A|undefined;

  constructor() {
    this.a = inject(A); // <- ✅
  }
}
@Component(...)
abstract class TestComponent {
  a: A|undefined;
 
  ngOnInit() {
    this.a = inject(A); // <- ❌
  }
}

kokkisajee

Extended developer diagnostics

{
  "angularCompilerOptions": {
    "extendedDiagnostics": {
      // The categories to use for specific diagnostics.
      "checks": {
        // Maps check name to its category.
        "invalidBananaInBox": "error"
        "nullishCoalescingNotNullable": "error"
      },
      // The category to use for any diagnostics
      // not listed in `checks` above.
      "defaultCategory": "suppress"
    },
    ...
  },
  ...
}

kokkisajee

npm install -g @angular/cli
// Installing Angular CLI
ng new my-app
// Creating a new Angular App
ng serve
// Starting local server

Let's Dive Into StandAlone Components

kokkisajee

// app.module.ts
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    RouterModule.forRoot([]),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
// app.component.ts
@Component({
  selector: 'sc-root',
  template: `
    <h1>Hello {{ title }}</h1>
  `
})
export class AppComponent {
  title = 'Angular';
}
// main.ts
if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch(err => console.error(err));

Hello 

kokkisajee

Bare Minimum Angular

AppModule

 

App

Component

Routing

Browser

Module

 

Forms

Module

 

main.ts

(bootstrap)

kokkisajee

Building Blocks of Angular

  • Module
  • Services
  • Component
  • Pipes
  • Directives

Mahela & Sanga are Certified Professional Developers

small scope

boundaries

plug-and-play

Everything is a component

kokkisajee

kokkisajee

Let's Dive Into StandAlone Components

DEVFEST is a Angular App - > These volunteers are the components

Hospitality

Speakers

Registration

Speakers

kokkisajee

 StandAlone Components

Hospitality

Registration

Speakers

kokkisajee

Flutter

Golang

Github

@NgModule({
  declarations: [
    AppComponent,
    AdminComponent,
    DashboardComponent,
    NavbarComponent,
    BarChartComponent,
    PieChartComponent,
    PieChartComponent,
    ProductComponent,
    RolesComponent,
    RoleDetailsComponent,
  ],
  imports: [
    MatToolbarModule,
    MatButtonModule,
    AppRoutingModule,
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    SharedModule,
    ...
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
  },
  {
    path: 'roles',
    children: [{
      path: '',
      component: RolesComponent,
    }, {
      path: 'details/:id',
      component: RolesDetailsComponent,
    }],
  },
  {
    path: 'admin',
    component: AdminComponent,
  },
  {
    path: 'product',
    component: ProductComponent,
  },
  {
    path: '**',
    redirectTo: 'dashboard'
  },
];

kokkisajee

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    HttpClientModule,
    SharedModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
const routes: Routes = [
  {
    path: 'dashboard',
    loadChildren: () => 
      import('./dashboard/dashboard.module')
       .then(m => m.DashboardModule),
  },
  {
    path: 'roles',
    loadChildren: () =>
      import('./roles/roles.module')
        .then(m => m.RolesModule),
  },
  {
    path: 'admin',
    loadChildren: () =>
      import('./admin/admin.module')
        .then(m => m.AdminModule),
  },
  {
    path: 'product',
    loadChildren: () =>
      import('./product/product.module')
        .then(m => m.ProductModule),
  },
  {
    path: '**',
    redirectTo: 'dashboard'
  },
];
@NgModule({
  declarations: [],
  imports: [
    RouterModule.forRoot(routes),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule { }

kokkisajee

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    MatToolbarModule,
    MatButtonModule,
    AppRoutingModule,
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    SharedModule,
    DashboardModule,
    ProductModule,
    RolesModule,
    SharedModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

😄

Still we have to improve

kokkisajee

Standalone Components 

  • Stepping towards Optional NgModule now
  • The component can be defined without NgModule
ng generate component standalone --standalone 
@Component({
  selector: 'app-standalone',
  standalone: true,
  imports: [CommonModule], // <-- all imports goes here
  templateUrl: './standalone.component.html',
  styleUrls: ['./standalone.component.scss']
})
export class StandaloneComponent implements OnInit {
  ...
}

kokkisajee

Goals

Non Goals

  • Another way to define component
  • The learning curve for a new developer would be less
  • Backward compatible
  • Library authors can take good benefits from it
  • Isolated component
  • Not a mandatory change to upgrade all components to standalone
  • Not a breaking change

kokkisajee

const routes = [
  {
    path: 'standalone',
    component: StandaloneComponent,
  },
  ...
];

Routing

Routing Lazy Load

const routes = [
  {
    path: 'standalone-lazy',
    loadComponent: () => import('./components/standalone-lazy.component')
    	.then(s => s.StandaloneLazyComponent),
  },
]

kokkisajee

// dashboard.module.ts
const matModules = [
  MatGridListModule,
  MatCardModule,
];

@NgModule({
  imports: [
    ...matModules,
    CommonModule,
    DashboardRoutingModule,
    SharedModule,
  ],
  declarations: [
    DashboardComponent
  ],
})
export class DashboardModule { }
<!--dashboard.component.html-->
<mat-grid-list cols="2">
    <mat-grid-tile>
        <mat-card>
            <app-bar-chart></app-bar-chart>
        </mat-card>
    </mat-grid-tile>
    <mat-grid-tile>
        <mat-card>
            <app-bar-chart></app-bar-chart>
        </mat-card>
    </mat-grid-tile>
</mat-grid-list>
//dashboard.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss'],

  
})
export class DashboardComponent {
}

imports: [...]​,

standalone: true,

kokkisajee

Standalone

+

Single File Component

kokkisajee

Migration Strategy

  • If you're using SCAM, it's very easy to migrate
  • Otherwise, new components can be created as a standalone
  • Library components can be converted to standalone.
  • You can use canMatch  on route level to migrate based on feature flag
import { 
  EditEmployeePage as NewEditEmployeePage,
} from './components/employees/edit-employee';
import { EditEmployeePage } from './employees/edit-employee';
...

provideRouter([
  {
    path: 'edit-employee/:id',
    component: NewEditEmployeePage,
    canMatch: () => inject(FeatureFlagService).isNewEditEmployeePageEnabled
  },
  {
    path: 'edit-employee/:id',
    component: EditEmployeePage
  }
])

kokkisajee

Demo WalkThrough

kokkisajee

Before I wind up

What Devfest has given me

2010

2015

2019

2018

2020

Attended first Devfest SL 2012 

Started with angular, became the top angularjs answerer on SO

Became the first google dev expert in the country

Started NG-Srilanka, First Angular Team meet at San Francisco

Book on Angular Projects,Top 4 contributors on Angular

2022

To produce 2 more GDEs from SL

Thank you and enjoy Defvest 2022!

 

kokkisajee

References

  • https://blog.angular.io/angular-v14-is-now-available-391a6db736af

  • https://netbasal.com/handling-page-titles-in-angular-40b53823af4a

  • https://blog.angular.io/angular-extended-diagnostics-53e2fa19ece9

  • https://marmicode.io/blog/angular-inject-and-injection-functions

  • https://nartc.me/blog/inheritance-angular-inject

kokkisajee

Standalone Components

By Sajeetharan Sinnathurai

Standalone Components

  • 385