Docs
/
Angular
Chapter 20
20 — Angular Libraries & Ecosystem
Essential Libraries
| Category | Library | Purpose |
|---|---|---|
| UI | Angular Material | Material Design components |
| UI | PrimeNG | Rich component suite (tables, charts, etc.) |
| UI | ng-zorro-antd | Ant Design for Angular |
| UI | Taiga UI | CDK-based, customizable UI kit |
| State | NgRx | Redux-style state management |
| State | NGXS | Simpler state management |
| State | Elf | Reactive store with RxJS |
| Forms | ngx-formly | Dynamic forms from JSON schema |
| HTTP | apollo-angular | GraphQL client |
| Auth | angular-auth-oidc-client | OAuth/OIDC |
| i18n | ngx-translate | Runtime translation |
| Charts | ngx-charts | D3-based charts |
| Charts | ng2-charts | Chart.js wrapper |
| Table | ag-Grid | Enterprise data grid |
| ngx-extended-pdf-viewer | PDF viewer | |
| Date | date-fns | Date utility (tree-shakable) |
| Animation | @angular/animations | Built-in animation API |
| Linting | angular-eslint | ESLint for Angular |
| Testing | Spectator | Simplified test setup |
| Monorepo | Nx | Monorepo tooling |
| Mobile | Ionic | Cross-platform mobile + web |
UI Libraries Comparison
| Material | PrimeNG | ng-zorro | Taiga UI | |
|---|---|---|---|---|
| Components | ~35 | 80+ | 60+ | 100+ |
| Theme system | SCSS mixins | CSS variables | Less variables | CSS variables |
| Design system | Material Design | Custom | Ant Design | Custom |
| Tree-shakable | ✅ | ✅ | ✅ | ✅ |
| Accessibility | Excellent | Good | Good | Good |
| Best for | Google/Material apps | Enterprise dashboards | Ant Design apps | Highly 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
| Need | Recommended |
|---|---|
| Simple form | Reactive Forms (built-in) |
| 50+ field dynamic forms | ngx-formly |
| Basic table | Material Table |
| Enterprise data grid (100k rows) | ag-Grid |
| Charts | ng2-charts (Chart.js) or ngx-charts (D3) |
| REST API | HttpClient (built-in) |
| GraphQL API | apollo-angular |
| Auth (OAuth/OIDC) | angular-auth-oidc-client |
| Date manipulation | date-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-formlyis a game-changer for config-driven forms (admin panels, CRMs)apollo-angularintegrates cleanly with Angular's DI and Observable patternsSpectatormakes component testing dramatically simpler- Always check bundle size impact before adding a library (
bundlephobia.com) - Prefer tree-shakable libraries (e.g.,
date-fnsovermoment,lodash-esoverlodash)