Docs
/
Angular
Chapter 20

20 — Angular Libraries & Ecosystem

Essential Libraries

CategoryLibraryPurpose
UIAngular MaterialMaterial Design components
UIPrimeNGRich component suite (tables, charts, etc.)
UIng-zorro-antdAnt Design for Angular
UITaiga UICDK-based, customizable UI kit
StateNgRxRedux-style state management
StateNGXSSimpler state management
StateElfReactive store with RxJS
Formsngx-formlyDynamic forms from JSON schema
HTTPapollo-angularGraphQL client
Authangular-auth-oidc-clientOAuth/OIDC
i18nngx-translateRuntime translation
Chartsngx-chartsD3-based charts
Chartsng2-chartsChart.js wrapper
Tableag-GridEnterprise data grid
PDFngx-extended-pdf-viewerPDF viewer
Datedate-fnsDate utility (tree-shakable)
Animation@angular/animationsBuilt-in animation API
Lintingangular-eslintESLint for Angular
TestingSpectatorSimplified test setup
MonorepoNxMonorepo tooling
MobileIonicCross-platform mobile + web

UI Libraries Comparison

MaterialPrimeNGng-zorroTaiga UI
Components~3580+60+100+
Theme systemSCSS mixinsCSS variablesLess variablesCSS variables
Design systemMaterial DesignCustomAnt DesignCustom
Tree-shakable
AccessibilityExcellentGoodGoodGood
Best forGoogle/Material appsEnterprise dashboardsAnt Design appsHighly customizable

Dynamic Forms — ngx-formly

Generate forms from JSON config — no manual template writing.

npm install @ngx-formly/core @ngx-formly/material
import { FormlyModule } from '@ngx-formly/core';
import { FormlyMaterialModule } from '@ngx-formly/material';

@Component({
  imports: [ReactiveFormsModule, FormlyModule, FormlyMaterialModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <formly-form [model]="model" [fields]="fields" [form]="form" />
      <button mat-raised-button type="submit">Submit</button>
    </form>
  `,
})
export class DynamicFormComponent {
  form = new FormGroup({});
  model = { email: '', name: '' };
  fields: FormlyFieldConfig[] = [
    {
      key: 'name',
      type: 'input',
      props: { label: 'Name', required: true, minLength: 2 },
    },
    {
      key: 'email',
      type: 'input',
      props: { label: 'Email', required: true },
      validators: { validation: [Validators.email] },
    },
    {
      key: 'role',
      type: 'select',
      props: {
        label: 'Role',
        options: [
          { label: 'User', value: 'user' },
          { label: 'Admin', value: 'admin' },
        ],
      },
    },
    {
      key: 'active',
      type: 'checkbox',
      props: { label: 'Active' },
    },
  ];

  onSubmit() { console.log(this.model); }
}

GraphQL — apollo-angular

npm install apollo-angular @apollo/client graphql
// app.config.ts
import { provideApollo } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import { InMemoryCache } from '@apollo/client/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideApollo(() => {
      const httpLink = inject(HttpLink);
      return {
        link: httpLink.create({ uri: '/graphql' }),
        cache: new InMemoryCache(),
      };
    }),
  ],
};
import { Apollo, gql } from 'apollo-angular';

const GET_USERS = gql`
  query GetUsers($limit: Int) {
    users(limit: $limit) { id name email role }
  }
`;

@Component({ ... })
export class UserListComponent {
  private apollo = inject(Apollo);

  users$ = this.apollo.watchQuery<{ users: User[] }>({
    query: GET_USERS,
    variables: { limit: 10 },
  }).valueChanges.pipe(map(result => result.data.users));
}

Simplified Testing — Spectator

npm install --save-dev @ngneat/spectator
import { createComponentFactory, Spectator } from '@ngneat/spectator';

describe('UserCardComponent', () => {
  let spectator: Spectator<UserCardComponent>;
  const createComponent = createComponentFactory({
    component: UserCardComponent,
    detectChanges: false,
  });

  beforeEach(() => {
    spectator = createComponent({ props: { user: mockUser } });
    spectator.detectChanges();
  });

  it('should display user name', () => {
    expect(spectator.query('h3')).toHaveText('Alice');
  });

  it('should emit on click', () => {
    let output: User | undefined;
    spectator.output('selected').subscribe(e => output = e as User);

    spectator.click('button');
    expect(output).toEqual(mockUser);
  });
});

Animations

import { trigger, state, style, transition, animate } from '@angular/animations';

@Component({
  animations: [
    trigger('fadeInOut', [
      transition(':enter', [
        style({ opacity: 0, transform: 'translateY(-10px)' }),
        animate('300ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
      ]),
      transition(':leave', [
        animate('200ms ease-in', style({ opacity: 0, transform: 'translateY(-10px)' })),
      ]),
    ]),
    trigger('slideToggle', [
      state('open', style({ height: '*', opacity: 1 })),
      state('closed', style({ height: '0', opacity: 0, overflow: 'hidden' })),
      transition('open <=> closed', animate('300ms ease-in-out')),
    ]),
  ],
  template: `
    @if (visible) {
      <div @fadeInOut>Animated content</div>
    }

    <div [@slideToggle]="isOpen ? 'open' : 'closed'">
      Collapsible content
    </div>
  `,
})

Choosing the Right Library

NeedRecommended
Simple formReactive Forms (built-in)
50+ field dynamic formsngx-formly
Basic tableMaterial Table
Enterprise data grid (100k rows)ag-Grid
Chartsng2-charts (Chart.js) or ngx-charts (D3)
REST APIHttpClient (built-in)
GraphQL APIapollo-angular
Auth (OAuth/OIDC)angular-auth-oidc-client
Date manipulationdate-fns (tree-shakable)
State (small-medium)Signals + services
State (large enterprise)NgRx SignalStore or NgRx Store

Key Takeaways

  • Angular has a rich ecosystem — avoid reinventing what libraries already solve
  • Choose UI libraries based on your design system (Material, Ant, Custom)
  • ngx-formly is a game-changer for config-driven forms (admin panels, CRMs)
  • apollo-angular integrates cleanly with Angular's DI and Observable patterns
  • Spectator makes component testing dramatically simpler
  • Always check bundle size impact before adding a library (bundlephobia.com)
  • Prefer tree-shakable libraries (e.g., date-fns over moment, lodash-es over lodash)