Viet Tran a38d04c3d5
feat(search): overhaul relevance and curated design data
Overhaul BM25 relevance, reasoning and data-quality contracts; refresh UI styles and framework guidance; add resilient text, chip, badge and micro-interaction guidance; strengthen release, provenance and catalog refresh gates; update bilingual documentation.
2026-08-14 00:08:23 +07:00

52 lines
19 KiB
CSV

No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL,Applies To,Status,Verified At
1,Components,Use standalone components,Standalone components are the default in current Angular,Standalone components for all new code,NgModule-based components for new projects,@Component({ imports: [CommonModule] }),@NgModule({ declarations: [MyComp] }),High,https://angular.dev/guide/components/importing,angular 22.x,active,2026-08-13
2,Components,Use signals for state,Signals are Angular's reactive primitive for fine-grained reactivity,Signals for component state over class properties,Mutable class properties without signals,count = signal(0); increment() { this.count.update(v => v + 1) },count = 0; increment() { this.count++ },High,https://angular.dev/guide/signals,angular 22.x,active,2026-08-13
3,Components,Use @if/@for/@switch control flow,Built-in control flow syntax replaces *ngIf/*ngFor directives,@if and @for in templates,*ngIf and *ngFor structural directives,@if (isLoggedIn) { <Dashboard /> } @else { <Login /> },"<div *ngIf=""isLoggedIn""><Dashboard /></div>",High,https://angular.dev/guide/templates/control-flow,angular 22.x,active,2026-08-13
4,Components,Use input() and output() component APIs,Use signal inputs and the output function for typed component contracts,input() and output() for new component APIs,Decorator-based inputs and outputs in new components,name = input<string>(); clicked = output<void>(),@Input() name: string; @Output() clicked = new EventEmitter(),High,https://angular.dev/guide/components/outputs,angular 22.x,active,2026-08-13
5,Components,Use content projection,ng-content for flexible component composition,ng-content with select for named slots,Rigid templates that can't be customized,"<ng-content select=""[header]"" /> <ng-content />","<div class=""header"">{{ title }}</div>",Medium,https://angular.dev/guide/components/content-projection,angular 22.x,active,2026-08-13
6,Components,Keep components small,Single responsibility; components should do one thing,Extract sub-components when template exceeds 50 lines,Monolithic components handling multiple concerns,<UserAvatar /> <UserDetails /> <UserActions />,One 300-line component template,Medium,https://angular.dev/guide/components,angular 22.x,active,2026-08-13
7,Components,Use OnPush where subtree skipping fits,Reduces checks for compatible component subtrees while signals still notify consumers,OnPush for stable input-driven components,Applying OnPush without understanding mutable input or event boundaries,changeDetection: ChangeDetectionStrategy.OnPush,items.push(nextItem) // same input reference,High,https://angular.dev/best-practices/skipping-subtrees,angular 22.x,active,2026-08-13
8,Components,Avoid direct DOM manipulation,Use renderer or ElementRef sparingly; prefer template bindings,Template bindings and Angular directives,Direct document.querySelector or innerHTML,"[class.active]=""isActive""",this.el.nativeElement.classList.add('active'),High,https://angular.dev/guide/components/host-elements,angular 22.x,active,2026-08-13
9,Routing,Lazy load feature routes,Load route chunks on demand to reduce initial bundle,loadComponent() for all feature routes,Eager-loaded routes in app config,{ path: 'admin' loadComponent: () => import('./admin/admin.component') },{ path: 'admin' component: AdminComponent },High,https://angular.dev/guide/routing/lazy-loading,angular 22.x,active,2026-08-13
10,Routing,Use route guards with functional API,Protect routes with canActivate/canMatch functional guards,Functional guards returning boolean or UrlTree,Class-based guards with CanActivate interface,canActivate: [() => inject(AuthService).isLoggedIn()],canActivate: [AuthGuard],High,https://angular.dev/guide/routing/common-router-tasks#preventing-unauthorized-access,angular 22.x,active,2026-08-13
11,Routing,Use route resolvers for data,Pre-fetch data before route activation using resolve,ResolveFn for route data,Fetching data in ngOnInit causing flash of empty state,resolve: { user: () => inject(UserService).getUser() },Fetch in ngOnInit with loading state flickering,Medium,https://angular.dev/guide/routing/common-router-tasks#resolve,angular 22.x,active,2026-08-13
12,Routing,Type route params with inject,Use inject(ActivatedRoute) with signals or toSignal,Typed route params via ActivatedRoute,Untyped route.snapshot.params string access,const id = toSignal(route.paramMap.pipe(map(p => p.get('id')))),const id = this.route.snapshot.params['id'],Medium,https://angular.dev/api/router/ActivatedRoute,angular 22.x,active,2026-08-13
13,Routing,Use nested routes for layouts,Compose shared layouts using router-outlet nesting,Nested routes with shared layout components,Duplicating layout code across routes,{ path: 'app' component: ShellComponent children: [...] },Duplicate header/sidebar in each route component,Medium,https://angular.dev/guide/routing/router-tutorial-toh#child-route-configuration,angular 22.x,active,2026-08-13
14,Routing,Configure preloading strategies,Preload lazy modules in background after initial load,PreloadAllModules or custom strategy,No preloading causing delayed navigation,provideRouter(routes withPreloading(PreloadAllModules)),provideRouter(routes),Low,https://angular.dev/api/router/PreloadAllModules,angular 22.x,active,2026-08-13
15,State,Use signals for local state,Signals provide synchronous reactive state without RxJS overhead,signal() for component-local reactive state,BehaviorSubject for simple local state,const items = signal<Item[]>([]); addItem(i: Item) { this.items.update(arr => [...arr i]) },items$ = new BehaviorSubject<Item[]>([]),High,https://angular.dev/guide/signals,angular 22.x,active,2026-08-13
16,State,Use computed() for derived state,Lazily evaluated derived values that update when dependencies change,computed() for values derived from other signals,Duplicated state or manual sync,readonly total = computed(() => this.items().reduce((s i) => s + i.price 0)),this.total = this.items.reduce(...) // called manually,High,https://angular.dev/guide/signals#computed-signals,angular 22.x,active,2026-08-13
17,State,Use effect() carefully,Effects run side effects when signals change; avoid overuse,effect() for side effects like logging or localStorage sync,effect() for deriving state (use computed instead),effect(() => localStorage.setItem('cart' JSON.stringify(this.cart()))),effect(() => { this.total.set(this.items().length) }),Medium,https://angular.dev/guide/signals#effects,angular 22.x,active,2026-08-13
18,State,Use NgRx Signal Store for complex state,NgRx Signal Store is the modern lightweight state management for Angular,@ngrx/signals SignalStore for feature state,Full NgRx reducer/action/effect boilerplate for simple state,const Store = signalStore(withState({ count: 0 }) withMethods(s => ({ increment: () => patchState(s { count: s.count() + 1 }) }))),createReducer(on(increment state => ({ ...state count: state.count + 1 }))),Medium,https://ngrx.io/guide/signals,angular 22.x,active,2026-08-13
19,State,Inject services for shared state,Services with signals share state across components without a store,Injectable service with signals for cross-component state,Prop drilling or @Input chains for shared state,@Injectable({ providedIn: 'root' }) class CartService { items = signal<Item[]>([]) },@Input() cartItems passed through 4 component levels,Medium,https://angular.dev/guide/di/creating-injectable-service,angular 22.x,active,2026-08-13
20,State,Avoid mixing RxJS and signals unnecessarily,Use toSignal() to bridge RxJS into signal world at the boundary,toSignal() to convert observable to signal at component edge,Subscribing in components and storing in signal manually,readonly user = toSignal(this.userService.user$),this.userService.user$.subscribe(u => this.user.set(u)),Medium,https://angular.dev/guide/rxjs-interop,angular 22.x,active,2026-08-13
21,Forms,Use typed reactive forms,FormGroup/FormControl with explicit generics for compile-time safety,FormBuilder with typed controls,Untyped FormControl or any casts,fb.group<LoginForm>({ email: fb.control('') password: fb.control('') }),new FormGroup({ email: new FormControl(null) }),High,https://angular.dev/guide/forms/typed-forms,angular 22.x,active,2026-08-13
22,Forms,Use reactive forms over template-driven,Reactive forms scale better and are fully testable,ReactiveFormsModule for all non-trivial forms,FormsModule with ngModel for complex forms,"<input [formControl]=""emailControl"" />","<input [(ngModel)]=""email"" />",Medium,https://angular.dev/guide/forms/reactive-forms,angular 22.x,active,2026-08-13
23,Forms,Write custom validators as functions,Functional validators are composable and tree-shakeable,ValidatorFn functions for custom validation,Class-based validators implementing Validator interface,const noSpaces: ValidatorFn = ctrl => ctrl.value?.includes(' ') ? { noSpaces: true } : null,class NoSpacesValidator implements Validator { validate(c) {} },Medium,https://angular.dev/guide/forms/form-validation#custom-validators,angular 22.x,active,2026-08-13
24,Forms,Use updateOn for performance,Control when validation runs to avoid per-keystroke validation overhead,updateOn: 'blur' or 'submit' for expensive validators,Default updateOn: 'change' for async validators,fb.control('' { updateOn: 'blur' validators: [Validators.email] }),fb.control('' [Validators.email]) // validates on every key,Low,https://angular.dev/api/forms/AbstractControl#updateOn,angular 22.x,active,2026-08-13
25,Forms,Use FormArray for dynamic fields,FormArray manages variable-length lists of controls,FormArray for add/remove field scenarios,Manually tracking index-based controls,get items(): FormArray { return this.form.get('items') as FormArray },items: [FormControl] managed outside form,Medium,https://angular.dev/guide/forms/reactive-forms#using-the-formarray-class,angular 22.x,active,2026-08-13
26,Forms,Display validation errors clearly,Use form control touched and dirty states to show errors at the right time,Show errors after field is touched,Show all errors on page load,@if (email.invalid && email.touched) { <span>Invalid email</span> },@if (email.invalid) { <span>Invalid email</span> },Medium,https://angular.dev/guide/forms/form-validation,angular 22.x,active,2026-08-13
27,Performance,Prepare components for zoneless change detection,Use signals markForCheck AsyncPipe and bound listeners so Angular receives change notifications,Notification-driven state updates,Depending on ZoneJS to notice arbitrary state mutation,count.set(count() + 1),this.count++ // outside a notifying Angular boundary,High,https://angular.dev/guide/zoneless,angular 22.x,active,2026-08-13
28,Performance,Use trackBy in @for blocks,Stable identity for list items prevents full DOM re-creation on change,track item.id in @for,track $index for dynamic data,@for (item of items; track item.id) { <li>{{ item.name }}</li> },@for (item of items; track $index) { <li>{{ item.name }}</li> },High,https://angular.dev/guide/templates/control-flow#track-and-identity,angular 22.x,active,2026-08-13
29,Performance,Use @defer for below-the-fold content,Defer blocks lazy-load components when they enter the viewport,@defer with on viewport for non-critical UI,Eagerly loading all components at startup,@defer (on viewport) { <HeavyChart /> } @placeholder { <Skeleton /> },<HeavyChart /> loaded at startup,High,https://angular.dev/guide/defer,angular 22.x,active,2026-08-13
30,Performance,Use NgOptimizedImage,Enforces image best practices: lazy loading LCP hints and proper sizing,NgOptimizedImage for all img tags,Plain img tags for CMS or user content,"<img ngSrc=""/hero.jpg"" width=""800"" height=""400"" priority />","<img src=""/hero.jpg"" />",High,https://angular.dev/guide/image-optimization,angular 22.x,active,2026-08-13
31,Performance,Tree-shake unused Angular features,Import only what you use from Angular packages,Import specific Angular modules needed,Import BrowserAnimationsModule when not using animations,import { NgOptimizedImage } from '@angular/common',import { CommonModule } from '@angular/common' // entire module,Medium,https://angular.dev/tools/cli/build,angular 22.x,active,2026-08-13
32,Performance,Avoid subscribe in components,Subscriptions leak and cause bugs; prefer async pipe or toSignal,toSignal() or async pipe instead of manual subscribe,Manual subscribe without unsubscribe in ngOnDestroy,readonly data = toSignal(this.service.data$),this.service.data$.subscribe(d => this.data = d),High,https://angular.dev/guide/rxjs-interop,angular 22.x,active,2026-08-13
33,Performance,Use @angular/ssr for hybrid rendering,Server rendering and prerendering improve LCP and SEO for public routes,Configure SSR or prerender per route,Pure CSR for SEO-critical pages,ng add @angular/ssr,"// no SSR, client renders empty shell",Medium,https://angular.dev/guide/ssr,angular 22.x,active,2026-08-13
34,Performance,Minimize bundle with standalone APIs,Standalone components + provideRouter() eliminate dead NgModule code,provideRouter() and provideHttpClient() in app.config,Root AppModule with all imports,provideRouter(routes) in app.config.ts,@NgModule({ imports: [RouterModule.forRoot(routes)] }),Medium,https://angular.dev/guide/routing/standalone,angular 22.x,active,2026-08-13
35,Testing,Use TestBed for component tests,TestBed sets up Angular DI for realistic component testing,TestBed.configureTestingModule for component tests,Instantiate components with new keyword,TestBed.configureTestingModule({ imports: [MyComponent] }),const comp = new MyComponent(),High,https://angular.dev/guide/testing/components-basics,angular 22.x,active,2026-08-13
36,Testing,Use Angular CDK component harnesses,Harnesses provide a stable testing API that survives template refactors,MatButtonHarness and custom HarnessLoader,Direct native element queries that break on template changes,const btn = await loader.getHarness(MatButtonHarness),fixture.debugElement.query(By.css('button')),Medium,https://material.angular.io/cdk/test-harnesses/overview,angular 22.x,active,2026-08-13
37,Testing,Use Spectator for less boilerplate,Spectator wraps TestBed with a cleaner API reducing test setup noise,Spectator for unit tests,Raw TestBed for every test,const spectator = createComponentFactory(MyComponent),TestBed.configureTestingModule({ declarations: [MyComponent] providers: [...] }),Low,https://github.com/ngneat/spectator,angular 22.x,active,2026-08-13
38,Testing,Mock services with jasmine.createSpyObj,Isolate unit tests by providing mock implementations of dependencies,SpyObj or jest.fn() mocks for services,Real HTTP calls in unit tests,const spy = jasmine.createSpyObj('UserService' ['getUser']); spy.getUser.and.returnValue(of(user)),providers: [UserService] // real service in unit test,High,https://angular.dev/guide/testing/services,angular 22.x,active,2026-08-13
39,Testing,Write integration tests for routes,Test full route navigation including guards and resolvers,RouterTestingHarness for route integration tests,Mock all routing behavior in unit tests,const harness = await RouterTestingHarness.create(); await harness.navigateByUrl('/home'),// manually calling route guard methods,Medium,https://angular.dev/api/router/testing/RouterTestingHarness,angular 22.x,active,2026-08-13
40,Testing,Test signal-based components,Signals update synchronously; no async flush needed in most cases,Read signal value directly in test assertions,TestBed.tick() or fakeAsync for signal reads,component.count.set(5); expect(component.double()).toBe(10),fakeAsync(() => { component.count.set(5); tick(); expect(component.double()).toBe(10) }),Medium,https://angular.dev/guide/testing,angular 22.x,active,2026-08-13
41,Styling,Use ViewEncapsulation.Emulated,Default emulation scopes styles to component preventing global leaks,Emulated or None for intentional global styles,ViewEncapsulation.None for component-specific styles,ViewEncapsulation.Emulated (default),ViewEncapsulation.None on feature components,Medium,https://angular.dev/guide/components/styling#style-scoping,angular 22.x,active,2026-08-13
42,Styling,Use :host selector,Style the component's host element using :host pseudo-class,:host for host element styles,Adding wrapper div just for styling,:host { display: block; padding: 1rem },"<div class=""wrapper"">...</div> + .wrapper { padding: 1rem }",Medium,https://angular.dev/guide/components/styling#host-element,angular 22.x,active,2026-08-13
43,Styling,Use CSS custom properties for theming,CSS variables work across component boundaries and enable dynamic theming,CSS custom properties for colors and spacing,Hardcoded hex values in component styles,:root { --primary: #6200ee } button { background: var(--primary) },button { background: #6200ee },Medium,https://angular.dev/guide/components/styling,angular 22.x,active,2026-08-13
44,Styling,Integrate Tailwind with Angular,Tailwind utilities work alongside Angular's ViewEncapsulation via global stylesheet,Add Tailwind in styles.css and use utility classes in templates,Custom CSS for layout that Tailwind already handles,"<div class=""flex items-center gap-4 p-6"">","<div class=""my-custom-flex""> /* .my-custom-flex { display: flex } */",Low,https://tailwindcss.com/docs/guides/angular,angular 22.x,active,2026-08-13
45,Styling,Use Angular Material theming tokens,Material 3 uses design tokens for systematic theming,M3 token-based theming for Angular Material,Overriding Angular Material CSS with deep selectors,@include mat.button-theme($my-theme),::ng-deep .mat-button { background: red },Medium,https://material.angular.io/guide/theming,angular 22.x,active,2026-08-13
46,Architecture,Use injection tokens for config,Provide configuration via InjectionToken for testability and flexibility,InjectionToken for environment-specific values,Importing environment.ts directly in services,const API_URL = new InjectionToken<string>('apiUrl'); provide: [{ provide: API_URL useValue: env.apiUrl }],constructor(private env: Environment) { this.url = env.apiUrl },Medium,https://angular.dev/guide/di/dependency-injection-providers#using-an-injectiontoken-object,angular 22.x,active,2026-08-13
47,Architecture,Use HTTP interceptors,Intercept requests for auth headers error handling and logging,Functional interceptors with withInterceptors(),Service-level header management in every request,withInterceptors([authInterceptor errorInterceptor]),httpClient.get(url { headers: { Authorization: token } }) in every call,High,https://angular.dev/guide/http/interceptors,angular 22.x,active,2026-08-13
48,Architecture,Organize by feature not type,Feature-based folder structure scales better than type-based,Feature folders with collocated component service and routes,Flat folders: all-components/ all-services/,src/features/checkout/checkout.component.ts checkout.service.ts checkout.routes.ts,src/components/checkout.component.ts src/services/checkout.service.ts,Medium,https://angular.dev/style-guide#folders-by-feature-structure,angular 22.x,active,2026-08-13
49,Architecture,Use environment configurations,Separate environment values for dev staging and prod via Angular build configs,angular.json fileReplacements for env configs,Hardcoded API URLs or feature flags in source,fileReplacements: [{ replace: environment.ts with: environment.prod.ts }],const API = 'https://api.example.com' // hardcoded in service,High,https://angular.dev/tools/cli/environments,angular 22.x,active,2026-08-13
50,Architecture,Prefer inject() over constructor DI,inject() function is composable and works in more contexts than constructor injection,inject() for dependency injection,Constructor parameters for new code,readonly http = inject(HttpClient); readonly router = inject(Router),constructor(private http: HttpClient private router: Router) {},Medium,https://angular.dev/api/core/inject,angular 22.x,active,2026-08-13