From harness-claude
Authors Angular components with signal inputs/outputs, OnPush change detection, lifecycle hooks, ViewChild queries, and proper cleanup. Use when creating components, wiring parent-child communication, or choosing strategies.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Author Angular components with correct inputs/outputs, change detection strategy, and lifecycle hooks
Creates Angular v20+ standalone components with signal inputs/outputs, OnPush detection, host bindings, content projection. Use for new UI components or refactoring class-based inputs.
Manages reactive state in Angular 17+ using signal(), computed(), effect(), and toSignal() for fine-grained, zone-free reactivity without manual subscriptions.
Guides modern Angular 17+ development: standalone components, signals, new control flow, RxJS, DI, routing, forms. Consults patterns.md for creation, sharp_edges.md for diagnosis, validations.md for review.
Share bugs, ideas, or general feedback.
Author Angular components with correct inputs/outputs, change detection strategy, and lifecycle hooks
Default and OnPush change detection strategiesngOnInit, ngOnDestroy, ngAfterViewInit) correctly@ViewChild or @ContentChild@Component and always set selector, template/templateUrl, and changeDetection.changeDetection: ChangeDetectionStrategy.OnPush for all new components — it prevents unnecessary re-renders and forces explicit data flow.input() signal function (Angular 17+) or @Input() decorator. Prefer signal inputs for new code.output() (Angular 17+) or @EventEmitter with @Output(). Signal outputs compose with effects naturally.OnDestroy (or use DestroyRef) to clean up subscriptions, timers, and manual DOM listeners.@ViewChild to access child component or DOM element references after ngAfterViewInit. Never access them in ngOnInit — they are not yet rendered.ViewEncapsulation.Emulated (the default) unless you have a specific reason to pierce the shadow DOM.import {
ChangeDetectionStrategy,
Component,
input,
output,
computed,
OnDestroy,
} from '@angular/core';
export interface Product {
id: string;
name: string;
price: number;
}
@Component({
selector: 'app-product-card',
template: `
<div class="card">
<h2>{{ product().name }}</h2>
<p>{{ formattedPrice() }}</p>
<button (click)="onAdd()">Add to cart</button>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductCardComponent implements OnDestroy {
// Signal input — reactive by default
product = input.required<Product>();
// Derived value — recalculates only when product() changes
formattedPrice = computed(() =>
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
this.product().price
)
);
// Typed output
addToCart = output<Product>();
onAdd(): void {
this.addToCart.emit(this.product());
}
ngOnDestroy(): void {
// Clean up any manual subscriptions here
}
}
Change detection in depth: Angular's default change detection walks the entire component tree on every browser event. OnPush restricts checks to components whose input references have changed, an async pipe resolves, or a signal emits. This makes rendering O(changed subtree) instead of O(entire tree). Adopt it globally by setting it as the default in angular.json under schematics.
Signal inputs vs @Input(): Signal inputs (Angular 17.1+) return a Signal<T> rather than a plain value. This means the input participates in the reactivity graph — computed() and effect() can read it without subscribing to a subject or change detection cycle. Prefer input() for new components; @Input() still works and is required for libraries targeting older Angular versions.
Lifecycle hook order:
ngOnChanges — fires before ngOnInit and on every @Input() changengOnInit — fires once after first ngOnChanges; safe to read inputs but NOT view childrenngAfterViewInit — fires after the component's view and child views are initialized; safe to use @ViewChildngOnDestroy — fires just before the component is removed; clean up subscriptions and effectsViewChild timing pitfall: Accessing a @ViewChild in ngOnInit returns undefined because the view hasn't been created yet. Move that logic to ngAfterViewInit.
Output naming: Angular convention for outputs is camelCase event names without the on prefix in the class, but HTML consumers use (addToCart). Avoid naming outputs with the on prefix in the property name — it creates (onAddToCart) which reads redundantly in templates.
Host bindings: Use host: { '[class.active]': 'isActive()' } in the decorator instead of @HostBinding — it is more declarative and compatible with the component metadata compiler.
https://angular.dev/guide/components