"Directive" is a broader category in Angular than it might first appear — every custom component is technically a directive with a template attached. Angular groups directives into three types based on what they actually do to the DOM, and knowing which category something falls into clarifies a lot about how it's meant to be used.
1. Component directives
@Component({
selector: 'app-product-card',
template: `<div class="card">{{ product.name }}</div>`,
})
export class ProductCardComponent {
@Input() product!: Product;
}
A component is a directive with its own template — the most familiar and heavily-used kind, but structurally it's still a directive at its core, just one that renders its own markup rather than modifying an existing element.
2. Structural directives — add or remove elements from the DOM
<div *ngIf="isVisible">Shown conditionally</div>
<li *ngFor="let item of items">{{ item.name }}</li>
Covered in more depth in an earlier look at *ngIf and *ngFor specifically — structural directives are marked with a leading * and change the actual DOM structure: adding, removing, or repeating elements entirely, not just adjusting an existing element's appearance.
3. Attribute directives — change appearance or behavior of an existing element
<p [ngClass]="{ 'is-active': isActive }">Text</p>
<p appHighlight>Custom directive example</p>
Also covered in more detail in building custom directives — an attribute directive modifies the element it's applied to (styling, behavior, event handling) without adding or removing anything from the DOM structure itself, which is the key distinction from a structural directive.
4. Why the three-way split matters practically
Recognizing which category a given directive falls into predicts how it behaves and how it's written: a structural directive needs the * syntax (or the more verbose <ng-template> form it desugars to) and typically implements TemplateRef/ViewContainerRef logic to add or remove DOM; an attribute directive is generally simpler, working through ElementRef and @HostListener/@HostBinding to affect an element already present in the DOM.
5. How ngModel fits into this
<input [(ngModel)]="username">
ngModel is an attribute directive — it doesn't add or remove the input element from the DOM; it attaches two-way binding behavior (reading and writing the input's value) to an element that's already there, which is exactly the attribute-directive pattern: modifying behavior on an existing element rather than controlling whether that element exists at all.
6. A quick reference
| Type | Purpose | Examples |
|---|---|---|
| Component | Has its own template | Any custom @Component |
| Structural | Adds/removes DOM elements | *ngIf, *ngFor, *ngSwitch |
| Attribute | Changes appearance/behavior of an existing element | ngClass, ngStyle, ngModel, custom directives |
Understanding this taxonomy is mostly useful for deciding what to build when creating something custom — a reusable piece of markup is a component; reusable conditional DOM logic is a structural directive; reusable behavior attached to existing markup is an attribute directive.