component-structure-new
Pattern of rule component-structure. Load when creating or editing *.component.ts — the ready-made decorator with property order, import grouping, class field layout, template conventions and qa-datai
SKILL.md.claude/skills/component-structure-new/SKILL.md
GitHub<!-- rt-kit v0.35.0 · patterns/component-structure-new.md · a1cf27649e96 · правится надстройкой, не здесь -->
The component file
Pattern of the rule component-structure. What must be true — the law
docs/constitution/frontend-application.md.
When to use
- A new component is created.
- The decorator, the import list or the template of an existing one is edited.
The decorator: order of properties
@Component({
selector: '<prefix>-component-name', // 1. selector
templateUrl: './component-name.component.html', // 2. template
styleUrl: './component-name.component.scss', // 3. style, singular
changeDetection: ChangeDetectionStrategy.OnPush, // 4. change detection strategy
imports: [
// 5. imports, in groups
// angular
FormsModule,
// rt-tools
BlockDirective,
ElemDirective,
// components
SomeChildComponent,
],
providers: [], // 6. providers
host: { class: '<prefix>-component-name' }, // 7. host bindings
})
export class ComponentNameComponent {
readonly #someService: SomeService = inject(SomeService);
public readonly data: InputSignal<Item[]> = input.required<Item[]>();
public readonly save: OutputEmitterRef<void> = output<void>();
protected readonly myButton: Signal<ElementRef | undefined> = viewChild<ElementRef>('button');
protected readonly items: WritableSignal<Item[]> = signal<Item[]>([]);
protected readonly itemCount: Signal<number> = computed((): number => this.items().length);
}
The grouping comments in imports are mandatory: // angular, // rt-tools, // components,
// directives, // pipes.
The template
- Self-closing tags on components without content:
<<prefix>-gallery />. - No extra wrappers —
:hostworks as the root, the block class comes withhost: { class: … }. - A complex template declares the block
<ng-container rtBlock="component-name">at the root. - One component in both branches of an
@ifis a conditional binding:
<!-- ✗ -->
@if (isRangeMode()) { <<prefix>-calendar [rangeMode]="true" /> } @else { <<prefix>-calendar [rangeMode]="false" /> }
<!-- ✓ -->
<<prefix>-calendar [rangeMode]="isRangeMode()" />
- Scrolling within the document — through the router, not
href="#id":
<a fragment="booking" [routerLink]="[]">…</a>
qa-dataid — on every interactive element
<button <prefix>Button qa-dataid="calendar-retry-prices" type="button" (click)="retryPrices.emit()">Повторить</button>
<div rtElem="grid" qa-dataid="admin-calendar-grid"></div>
- The value — kebab-case by the meaning of the element, without the name of the wrapping
component:
calendar-day,booking-submit. - Uniqueness — within the screen; repeating list elements carry one anchor and differ through
data-*([attr.data-iso],[attr.data-state]). - A decorative element is marked
qa-skipon the tag itself. - On a component of the kit the anchor sits on the host, while for a directive the host is the
element itself. The component's test aims inside —
[qa-dataid="x"] button— and for a directive that lands in emptiness: there is no nested button under the host. They differ only in what the tag is marked with, and the anchor value does not show it. - Replacing a wrapper component with a directive starts with a search for the anchor over the end-to-end suites. The markup looks whole after such a replacement, the anchor stays the same, and the test goes red on an empty selection — and it goes red for whoever did not touch the markup.
Common misses
selector: 'app-component'instead of<prefix>-component.styleUrls: ['./component.scss']instead ofstyleUrl— the plural is wrong here.- A method call in a binding:
{{ getTotal() }}is refused by the linter. The replacement iscomputed(), and where the value comes from the template context — a pure pipe. - A wrapper that exists only to be a flex or grid container around all the children: its layout
moves to
:host. - A deep relative import between libs instead of
@<scope>/<family>/<domain>/<layer>. - Own markup instead of a ready-made kit component — rule
reuse-first.