Files
tsl-devkit/skills/thirdparty/ui-ux-pro-max/data/stacks/angular.csv
T
csh 3d83740f88 Squashed 'docs/standards/playbook/' changes from c3f8137..25d895d
25d895d 🐛 fix(gitea_workflow): clean up temp repos after job steps
2bc3b11 🐛 fix(gitea_workflow): clean up temporary repo dirs in workflows
98c3f30 📝 docs(agent_rules): allow plan execution on current branch
16c7230 📝 docs(prompts): define custom verify layering
8efc4dd 🐛 fix(skills): quote commit-message description
bc8498f 🐛 fix(ci): install tomli for gitea tests
55cda3b 🐛 fix(tests): report missing toml parser clearly
c0729c7 🐛 fix(playbook): import Optional for cli compatibility
63e24bf 📦 deps(skills): sync thirdparty skills
d2f9356 🐛 fix(ci): isolate gitea workflow repos
588b81d 🐛 fix(ci): inline gitea workflow bootstrap
e0b1c3a ♻️ refactor(skills): standardize first-party skill contracts
2c5050d ♻️ refactor(skills): rename repo skills source dir
f049dfb 📦 deps(skills): drop duplicate first-party superpowers skills
234b335  feat(workflow): add superpowers planning and execution state tracking
c1702a6 📝 docs(markdown): format tracked markdown and drop stale templates
2325409 📝 docs(markdown): clarify optional markdownlint usage
214c44e 🔧 chore(markdown): add markdownlint baseline and lint fixes
a22b324 📝 docs(templates): add execution and memory-bank prompt templates
223a797 📝 docs(templates): update README for Claude Code and current features
4ac8672 📝 docs: simplify README + platform-agnostic tools + auto-create local rules
2431c9d 📝 docs: add claude_md config and use cross-platform paths
d64b248 📝 docs: fix README.md inaccuracies and add Claude Code info
c8d6bf2 🐛 fix(playbook): use relative paths in CLAUDE.md when not at project root
6518f0f  feat(playbook): auto-create CLAUDE.md with path discovery
6ec9a45  feat(skills): add skill_link symlink support + platform-agnostic prompt
9f8b6b5 📝 docs: update README and config example for Claude Code support
79cff6c 📝 docs(skills): add Claude Code platform support
452c6f5  feat(playbook): auto-inject AGENTS.md into CLAUDE.md
e1dbf3c 🐛 fix(skills): remove dual-path from commit-message skill
f3a7259 🔧 chore(ci): use prepare_repo.sh in both workflows
da08212 🔧 chore(ci): extract prepare_repo.sh and clean up workflows
7ade85e 🗑️ remove(tsl): drop syntax_book/, data/ source and build script
f94dba0 ♻️ refactor(skills): update playbook.py and tests for thirdparty/ layout
b3df412 ♻️ refactor(skills): separate thirdparty skills into thirdparty/ subdirectory
64950e7 📦 deps(skills): sync thirdparty skills
a2e3cb0  feat(playbook): add no_backup deploy controls
8609d59 🐛 fix(docs): repair reference catalog source links
956da11 🐛 fix(playbook): publish hidden ci test fixes
3f67754 📦 deps(skills): sync thirdparty skills
08ca87b 📦 deps(skills): add karpathy thirdparty sync
96b705b 📝 docs(tsl): rebuild canonical syntax and routing manual
3ed5052 📦 deps(skills): sync thirdparty skills
60108dd 📦 deps(skills): sync thirdparty skills
da85d4e 🐛 fix(thirdparty): prune nested project snapshots
a2a697e 📦 deps(skills): sync thirdparty skills
9df610a 🐛 fix(thirdparty): exclude duplicated superpowers skills
33dd5bb 🐛 fix(thirdparty): preserve optional manifest fields
91b0ea7 🐛 fix(thirdparty): preserve manifest during snapshot update
2e26f98 🔧 chore(thirdparty): generalize skills sync pipeline
5b9c1e3 📦 deps(skills): sync superpowers
2f2d34a 📝 docs(readme): normalize subtree command spacing
62db7db 🐛 fix(ci): serialize superpowers update and sync
3463223 🐛 fix(ci): use literal superpowers sync paths
48f6de8 📦 deps(skills): sync superpowers
4b23529 🔧 chore(ci): merge superpowers update and sync workflow
a56d75b 📦 deps(skills): sync superpowers
84bcefa 🔧 chore(ci): use ci[bot] commit author name
00a07e5 📦 deps(skills): sync superpowers
7b84daf 🐛 fix(templates): enforce main loop progress tracking
51373d7 🔧 chore(ci): automate superpowers sync workflow
eaaa39c 🐛 fix(ci): prevent stale superpowers sync from restoring skills block
79755c6 📦 deps(skills): sync superpowers
836d878 📦 deps(skills): sync superpowers
8216c9f 📦 deps(skills): sync superpowers
9439505 🐛 fix(playbook): address reported repo issues

git-subtree-dir: docs/standards/playbook
git-subtree-split: 25d895d8b3f56624ccfe99ad7289e9eb49e0f316
2026-05-24 13:04:14 +08:00

18 KiB

1NoCategoryGuidelineDescriptionDoDon'tCode GoodCode BadSeverityDocs URL
21ComponentsUse standalone componentsAngular 17+ default; no NgModule neededStandalone components for all new codeNgModule-based components for new projects@Component({ standalone: true imports: [CommonModule] })@NgModule({ declarations: [MyComp] })Highhttps://angular.dev/guide/components/importing
32ComponentsUse signals for stateSignals are Angular's reactive primitive for fine-grained reactivitySignals for component state over class propertiesMutable class properties without signalscount = signal(0); increment() { this.count.update(v => v + 1) }count = 0; increment() { this.count++ }Highhttps://angular.dev/guide/signals
43ComponentsUse @if/@for/@switch control flowBuilt-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>Highhttps://angular.dev/guide/templates/control-flow
54ComponentsUse input() and output() signalsSignal-based inputs/outputs replace @Input()/@Output() decoratorsinput() and output() for component API@Input() and @Output() decoratorsname = input<string>(); clicked = output<void>()@Input() name: string; @Output() clicked = new EventEmitter()Highhttps://angular.dev/guide/components/inputs
65ComponentsUse content projectionng-content for flexible component compositionng-content with select for named slotsRigid templates that can't be customized<ng-content select="[header]" /> <ng-content /><div class="header">{{ title }}</div>Mediumhttps://angular.dev/guide/components/content-projection
76ComponentsKeep components smallSingle responsibility; components should do one thingExtract sub-components when template exceeds 50 linesMonolithic components handling multiple concerns<UserAvatar /> <UserDetails /> <UserActions />One 300-line component templateMediumhttps://angular.dev/guide/components
87ComponentsUse OnPush change detectionReduces re-renders by only checking on input changes or signal updatesOnPush for all componentsDefault change detection strategychangeDetection: ChangeDetectionStrategy.OnPushchangeDetection: ChangeDetectionStrategy.DefaultHighhttps://angular.dev/guide/components/lifecycle
98ComponentsAvoid direct DOM manipulationUse renderer or ElementRef sparingly; prefer template bindingsTemplate bindings and Angular directivesDirect document.querySelector or innerHTML[class.active]="isActive"this.el.nativeElement.classList.add('active')Highhttps://angular.dev/guide/components/host-elements
109RoutingLazy load feature routesLoad route chunks on demand to reduce initial bundleloadComponent() for all feature routesEager-loaded routes in app config{ path: 'admin' loadComponent: () => import('./admin/admin.component') }{ path: 'admin' component: AdminComponent }Highhttps://angular.dev/guide/routing/lazy-loading
1110RoutingUse route guards with functional APIProtect routes with canActivate/canMatch functional guardsFunctional guards returning boolean or UrlTreeClass-based guards with CanActivate interfacecanActivate: [() => inject(AuthService).isLoggedIn()]canActivate: [AuthGuard]Highhttps://angular.dev/guide/routing/common-router-tasks#preventing-unauthorized-access
1211RoutingUse route resolvers for dataPre-fetch data before route activation using resolveResolveFn for route dataFetching data in ngOnInit causing flash of empty stateresolve: { user: () => inject(UserService).getUser() }Fetch in ngOnInit with loading state flickeringMediumhttps://angular.dev/guide/routing/common-router-tasks#resolve
1312RoutingType route params with injectUse inject(ActivatedRoute) with signals or toSignalTyped route params via ActivatedRouteUntyped route.snapshot.params string accessconst id = toSignal(route.paramMap.pipe(map(p => p.get('id'))))const id = this.route.snapshot.params['id']Mediumhttps://angular.dev/api/router/ActivatedRoute
1413RoutingUse nested routes for layoutsCompose shared layouts using router-outlet nestingNested routes with shared layout componentsDuplicating layout code across routes{ path: 'app' component: ShellComponent children: [...] }Duplicate header/sidebar in each route componentMediumhttps://angular.dev/guide/routing/router-tutorial-toh#child-route-configuration
1514RoutingConfigure preloading strategiesPreload lazy modules in background after initial loadPreloadAllModules or custom strategyNo preloading causing delayed navigationprovideRouter(routes withPreloading(PreloadAllModules))provideRouter(routes)Lowhttps://angular.dev/api/router/PreloadAllModules
1615StateUse signals for local stateSignals provide synchronous reactive state without RxJS overheadsignal() for component-local reactive stateBehaviorSubject for simple local stateconst items = signal<Item[]>([]); addItem(i: Item) { this.items.update(arr => [...arr i]) }items$ = new BehaviorSubject<Item[]>([])Highhttps://angular.dev/guide/signals
1716StateUse computed() for derived stateLazily evaluated derived values that update when dependencies changecomputed() for values derived from other signalsDuplicated state or manual syncreadonly total = computed(() => this.items().reduce((s i) => s + i.price 0))this.total = this.items.reduce(...) // called manuallyHighhttps://angular.dev/guide/signals#computed-signals
1817StateUse effect() carefullyEffects run side effects when signals change; avoid overuseeffect() for side effects like logging or localStorage synceffect() for deriving state (use computed instead)effect(() => localStorage.setItem('cart' JSON.stringify(this.cart())))effect(() => { this.total.set(this.items().length) })Mediumhttps://angular.dev/guide/signals#effects
1918StateUse NgRx Signal Store for complex stateNgRx Signal Store is the modern lightweight state management for Angular@ngrx/signals SignalStore for feature stateFull NgRx reducer/action/effect boilerplate for simple stateconst Store = signalStore(withState({ count: 0 }) withMethods(s => ({ increment: () => patchState(s { count: s.count() + 1 }) })))createReducer(on(increment state => ({ ...state count: state.count + 1 })))Mediumhttps://ngrx.io/guide/signals
2019StateInject services for shared stateServices with signals share state across components without a storeInjectable service with signals for cross-component stateProp drilling or @Input chains for shared state@Injectable({ providedIn: 'root' }) class CartService { items = signal<Item[]>([]) }@Input() cartItems passed through 4 component levelsMediumhttps://angular.dev/guide/di/creating-injectable-service
2120StateAvoid mixing RxJS and signals unnecessarilyUse toSignal() to bridge RxJS into signal world at the boundarytoSignal() to convert observable to signal at component edgeSubscribing in components and storing in signal manuallyreadonly user = toSignal(this.userService.user$)this.userService.user$.subscribe(u => this.user.set(u))Mediumhttps://angular.dev/guide/rxjs-interop
2221FormsUse typed reactive formsFormGroup/FormControl with explicit generics for compile-time safetyFormBuilder with typed controlsUntyped FormControl or any castsfb.group<LoginForm>({ email: fb.control('') password: fb.control('') })new FormGroup({ email: new FormControl(null) })Highhttps://angular.dev/guide/forms/typed-forms
2322FormsUse reactive forms over template-drivenReactive forms scale better and are fully testableReactiveFormsModule for all non-trivial formsFormsModule with ngModel for complex forms<input [formControl]="emailControl" /><input [(ngModel)]="email" />Mediumhttps://angular.dev/guide/forms/reactive-forms
2423FormsWrite custom validators as functionsFunctional validators are composable and tree-shakeableValidatorFn functions for custom validationClass-based validators implementing Validator interfaceconst noSpaces: ValidatorFn = ctrl => ctrl.value?.includes(' ') ? { noSpaces: true } : nullclass NoSpacesValidator implements Validator { validate(c) {} }Mediumhttps://angular.dev/guide/forms/form-validation#custom-validators
2524FormsUse updateOn for performanceControl when validation runs to avoid per-keystroke validation overheadupdateOn: 'blur' or 'submit' for expensive validatorsDefault updateOn: 'change' for async validatorsfb.control('' { updateOn: 'blur' validators: [Validators.email] })fb.control('' [Validators.email]) // validates on every keyLowhttps://angular.dev/api/forms/AbstractControl#updateOn
2625FormsUse FormArray for dynamic fieldsFormArray manages variable-length lists of controlsFormArray for add/remove field scenariosManually tracking index-based controlsget items(): FormArray { return this.form.get('items') as FormArray }items: [FormControl] managed outside formMediumhttps://angular.dev/guide/forms/reactive-forms#using-the-formarray-class
2726FormsDisplay validation errors clearlyUse form control touched and dirty states to show errors at the right timeShow errors after field is touchedShow all errors on page load@if (email.invalid && email.touched) { <span>Invalid email</span> }@if (email.invalid) { <span>Invalid email</span> }Mediumhttps://angular.dev/guide/forms/form-validation
2827PerformanceApply OnPush to all componentsOnPush + signals eliminates most unnecessary change detection cyclesOnPush change detection everywhereDefault strategy which checks entire tree on every eventchangeDetection: ChangeDetectionStrategy.OnPushchangeDetection: ChangeDetectionStrategy.DefaultHighhttps://angular.dev/best-practices/skipping-component-subtrees
2928PerformanceUse trackBy in @for blocksStable identity for list items prevents full DOM re-creation on changetrack item.id in @for@for (item of items; track item.id) { <li>{{ item.name }}</li> }@for (item of items; track $index) { <li>{{ item.name }}</li> }Highhttps://angular.dev/guide/templates/control-flow#track-and-identity
3029PerformanceUse @defer for below-the-fold contentDefer blocks lazy-load components when they enter the viewport@defer with on viewport for non-critical UIEagerly loading all components at startup@defer (on viewport) { <HeavyChart /> } @placeholder { <Skeleton /> }<HeavyChart /> loaded at startupHighhttps://angular.dev/guide/defer
3130PerformanceUse NgOptimizedImageEnforces image best practices: lazy loading LCP hints and proper sizingNgOptimizedImage for all img tagsPlain img tags for CMS or user content<img ngSrc="/hero.jpg" width="800" height="400" priority /><img src="/hero.jpg" />Highhttps://angular.dev/guide/image-optimization
3231PerformanceTree-shake unused Angular featuresImport only what you use from Angular packagesImport specific Angular modules neededImport BrowserAnimationsModule when not using animationsimport { NgOptimizedImage } from '@angular/common'import { CommonModule } from '@angular/common' // entire moduleMediumhttps://angular.dev/tools/cli/build
3332PerformanceAvoid subscribe in componentsSubscriptions leak and cause bugs; prefer async pipe or toSignaltoSignal() or async pipe instead of manual subscribeManual subscribe without unsubscribe in ngOnDestroyreadonly data = toSignal(this.service.data$)this.service.data$.subscribe(d => this.data = d)Highhttps://angular.dev/guide/rxjs-interop
3433PerformanceUse SSR with Angular UniversalPre-render pages for faster LCP and better SEOSSR or SSG for public-facing routesPure CSR for SEO-critical pagesng add @angular/ssr// no SSR, client renders empty shellMediumhttps://angular.dev/guide/ssr
3534PerformanceMinimize bundle with standalone APIsStandalone components + provideRouter() eliminate dead NgModule codeprovideRouter() and provideHttpClient() in app.configRoot AppModule with all importsprovideRouter(routes) in app.config.ts@NgModule({ imports: [RouterModule.forRoot(routes)] })Mediumhttps://angular.dev/guide/routing/standalone
3635TestingUse TestBed for component testsTestBed sets up Angular DI for realistic component testingTestBed.configureTestingModule for component testsInstantiate components with new keywordTestBed.configureTestingModule({ imports: [MyComponent] })const comp = new MyComponent()Highhttps://angular.dev/guide/testing/components-basics
3736TestingUse Angular CDK component harnessesHarnesses provide a stable testing API that survives template refactorsMatButtonHarness and custom HarnessLoaderDirect native element queries that break on template changesconst btn = await loader.getHarness(MatButtonHarness)fixture.debugElement.query(By.css('button'))Mediumhttps://material.angular.io/cdk/test-harnesses/overview
3837TestingUse Spectator for less boilerplateSpectator wraps TestBed with a cleaner API reducing test setup noiseSpectator for unit testsRaw TestBed for every testconst spectator = createComponentFactory(MyComponent)TestBed.configureTestingModule({ declarations: [MyComponent] providers: [...] })Lowhttps://github.com/ngneat/spectator
3938TestingMock services with jasmine.createSpyObjIsolate unit tests by providing mock implementations of dependenciesSpyObj or jest.fn() mocks for servicesReal HTTP calls in unit testsconst spy = jasmine.createSpyObj('UserService' ['getUser']); spy.getUser.and.returnValue(of(user))providers: [UserService] // real service in unit testHighhttps://angular.dev/guide/testing/services
4039TestingWrite integration tests for routesTest full route navigation including guards and resolversRouterTestingHarness for route integration testsMock all routing behavior in unit testsconst harness = await RouterTestingHarness.create(); await harness.navigateByUrl('/home')// manually calling route guard methodsMediumhttps://angular.dev/api/router/testing/RouterTestingHarness
4140TestingTest signal-based componentsSignals update synchronously; no async flush needed in most casesRead signal value directly in test assertionsTestBed.tick() or fakeAsync for signal readscomponent.count.set(5); expect(component.double()).toBe(10)fakeAsync(() => { component.count.set(5); tick(); expect(component.double()).toBe(10) })Mediumhttps://angular.dev/guide/testing
4241StylingUse ViewEncapsulation.EmulatedDefault emulation scopes styles to component preventing global leaksEmulated or None for intentional global stylesViewEncapsulation.None for component-specific stylesViewEncapsulation.Emulated (default)ViewEncapsulation.None on feature componentsMediumhttps://angular.dev/guide/components/styling#style-scoping
4342StylingUse :host selectorStyle the component's host element using :host pseudo-class:host for host element stylesAdding wrapper div just for styling:host { display: block; padding: 1rem }<div class="wrapper">...</div> + .wrapper { padding: 1rem }Mediumhttps://angular.dev/guide/components/styling#host-element
4443StylingUse CSS custom properties for themingCSS variables work across component boundaries and enable dynamic themingCSS custom properties for colors and spacingHardcoded hex values in component styles:root { --primary: #6200ee } button { background: var(--primary) }button { background: #6200ee }Mediumhttps://angular.dev/guide/components/styling
4544StylingIntegrate Tailwind with AngularTailwind utilities work alongside Angular's ViewEncapsulation via global stylesheetAdd Tailwind in styles.css and use utility classes in templatesCustom 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 } */Lowhttps://tailwindcss.com/docs/guides/angular
4645StylingUse Angular Material theming tokensMaterial 3 uses design tokens for systematic themingM3 token-based theming for Angular MaterialOverriding Angular Material CSS with deep selectors@include mat.button-theme($my-theme)::ng-deep .mat-button { background: red }Mediumhttps://material.angular.io/guide/theming
4746ArchitectureUse injection tokens for configProvide configuration via InjectionToken for testability and flexibilityInjectionToken for environment-specific valuesImporting environment.ts directly in servicesconst API_URL = new InjectionToken<string>('apiUrl'); provide: [{ provide: API_URL useValue: env.apiUrl }]constructor(private env: Environment) { this.url = env.apiUrl }Mediumhttps://angular.dev/guide/di/dependency-injection-providers#using-an-injectiontoken-object
4847ArchitectureUse HTTP interceptorsIntercept requests for auth headers error handling and loggingFunctional interceptors with withInterceptors()Service-level header management in every requestwithInterceptors([authInterceptor errorInterceptor])httpClient.get(url { headers: { Authorization: token } }) in every callHighhttps://angular.dev/guide/http/interceptors
4948ArchitectureOrganize by feature not typeFeature-based folder structure scales better than type-basedFeature folders with collocated component service and routesFlat folders: all-components/ all-services/src/features/checkout/checkout.component.ts checkout.service.ts checkout.routes.tssrc/components/checkout.component.ts src/services/checkout.service.tsMediumhttps://angular.dev/style-guide#folders-by-feature-structure
5049ArchitectureUse environment configurationsSeparate environment values for dev staging and prod via Angular build configsangular.json fileReplacements for env configsHardcoded API URLs or feature flags in sourcefileReplacements: [{ replace: environment.ts with: environment.prod.ts }]const API = 'https://api.example.com' // hardcoded in serviceHighhttps://angular.dev/tools/cli/environments
5150ArchitecturePrefer inject() over constructor DIinject() function is composable and works in more contexts than constructor injectioninject() for dependency injectionConstructor parameters for new codereadonly http = inject(HttpClient); readonly router = inject(Router)constructor(private http: HttpClient private router: Router) {}Mediumhttps://angular.dev/api/core/inject