Angular Components, Directives, and Services for Beginners

A clear, beginner-friendly walkthrough of the three building blocks of every Angular app. Learn how components, directives, and services work together, with simple 2026 examples and best practices.

Angularbeginner
13 min read

Every Angular app, from a tiny hobby site to Google's own admin consoles, is built from the same three pieces: components that show UI, directives that change how elements behave, and services that hold business logic and data. Master those three and the rest of the framework is just specialised versions of them.

This guide explains each in plain language, shows the modern 2026 syntax (standalone components, signals, the new control-flow blocks), and walks through a small example that uses all three together. By the end you will read any Angular file with confidence.

Components: The UI Building Block

A component is a TypeScript class with a template and a CSS scope. The class holds state and methods; the template renders the UI; Angular's compiler stitches them together.

index.tsindex.ts
import { Component, signal } from '@angular/core';
 
@Component({
  selector: 'user-card',
  standalone: true,
  template: `
    <div class="card">
      <h3>{{ name() }}</h3>
      <button (click)="follow()">Follow ({{ followers() }})</button>
    </div>
  `,
  styles: `.card { padding: 1rem; border-radius: .5rem; }`,
})
export class UserCardComponent {
  name = signal('Ada Lovelace');
  followers = signal(0);
  follow() { this.followers.update(v => v + 1); }
}

Three things to notice. standalone: true means the component declares its own dependencies — no NgModule needed. The template uses Angular's bindings: {{ x }} for interpolation, (event) for listeners, and [prop] for property binding. State is a signal() — read it with name(), update via set() or update(). <user-card /> (the selector) drops it into any other template.

Components compose like Lego: a Page component contains a Header, the Header contains a Logo and a Nav, each piece is independently testable.

Inputs and Outputs: Talking Between Components

Components communicate with input() (data flowing in) and output() (events flowing out). The function-style API is the modern 2026 default.

index.tsindex.ts
import { Component, input, output, computed } from '@angular/core';
 
@Component({
  selector: 'tag-pill',
  standalone: true,
  template: `<span [class.active]="isActive()">{{ label() }}</span>`,
})
export class TagPillComponent {
  label = input.required<string>();
  selected = input(false);
  toggle = output<string>();
  isActive = computed(() => this.selected());
}

A parent uses it like <tag-pill [label]="t" [selected]="t === current" (toggle)="setCurrent($event)" />. Square brackets bind a value into an input; round brackets listen to an output. This pattern is the entire data-flow contract in an Angular app.

Directives: Behaviour You Attach to Elements

A directive is a class that modifies an existing element instead of rendering a whole new one. There are three kinds and you will only commonly write one.

  • Components are technically directives that have a template. (You already know these.)
  • Structural directives change the DOM structure: add, remove, or repeat elements. The classics were *ngIf, *ngFor, *ngSwitch. In modern Angular these are replaced by control-flow blocks in templates: @if, @for, @switch. You almost never write structural directives yourself.
  • Attribute directives change appearance or behaviour without adding elements. This is the kind you might write — for example, a tooltip, an autofocus, or a "highlight on hover" directive.
index.tsindex.ts
import { Directive, ElementRef, HostListener, inject } from '@angular/core';
 
@Directive({ selector: '[appAutofocus]', standalone: true })
export class AutofocusDirective {
  private el = inject(ElementRef<HTMLElement>);
  ngOnInit() { this.el.nativeElement.focus(); }
}

Use it: <input appAutofocus />. The directive runs as the input renders and focuses it. Tiny pieces of behaviour like this are the right reason to write a directive — anything bigger should probably be a component.

The new 2026 control-flow syntax for the things you used to need structural directives for:

htmlhtml
@if (isLoggedIn()) {
  <app-dashboard />
} @else {
  <app-login />
}
 
@for (todo of todos(); track todo.id) {
  <li>{{ todo.title }}</li>
}

Cleaner, faster to render, and easier to read than *ngIf / *ngFor.

Services: Where Logic and Data Live

A service is a class that holds anything that should outlive a single component or be shared between components — fetching data, managing auth, talking to a websocket, holding a shopping cart.

index.tsindex.ts
import { Injectable, signal } from '@angular/core';
 
@Injectable({ providedIn: 'root' })
export class CartService {
  private items = signal<string[]>([]);
  readonly count = computed(() => this.items().length);
  add(id: string) { this.items.update(xs => [...xs, id]); }
}

providedIn: 'root' tells Angular to create one instance for the whole app and reuse it everywhere. To use it in a component, inject it:

index.tsindex.ts
import { Component, inject } from '@angular/core';
import { CartService } from './cart.service';
 
@Component({ /* ... */ })
export class HeaderComponent {
  cart = inject(CartService);
}

Now cart.count() is reactive in the template and cart.add(id) works from anywhere. This is dependency injection — a fancy term for "Angular hands the right instance to anyone who asks." It is the single most powerful pattern in the framework.

Putting Them Together: A Tiny Real Example

A search box that calls a service, an @if block to show results, and a directive to autofocus the input.

index.tsindex.ts
@Component({
  selector: 'app-search',
  standalone: true,
  imports: [AutofocusDirective],
  template: `
    <input appAutofocus (input)="onInput($event)" />
    @if (results().length) {
      <ul>@for (r of results(); track r.id) { <li>{{ r.name }}</li> }</ul>
    } @else { <p>Type to search…</p> }
  `,
})
export class SearchComponent {
  api = inject(SearchApi);
  results = signal<Result[]>([]);
  onInput(e: Event) {
    const q = (e.target as HTMLInputElement).value;
    this.api.find(q).then(r => this.results.set(r));
  }
}

Component renders the UI, directive adds behaviour, service does the work. Same shape from a 100-line app to a 100,000-line app.

Common Mistakes Beginners Make

  • Forgetting standalone: true on new components — older tutorials default to NgModules.
  • Mutating a signal's value in place (xs.push(...)). Always set a new value: signal.update(xs => [...xs, item]).
  • Subscribing to RxJS observables manually inside a component. Use the async pipe in templates or toSignal() to convert to a signal.
  • Putting fetch calls directly in a component. Move them into a service so they can be tested and reused.
  • Writing structural directives. Use the new @if / @for blocks instead.

Quick Reference

  • Component: @Component({ standalone: true, selector, template, styles })
  • Inputs/outputs: input<T>(), input.required<T>(), output<T>()
  • State: signal(), computed(), read with (), update with .set() / .update()
  • Control flow in templates: @if, @else, @for (x of xs; track x.id), @switch
  • Service: class marked @Injectable({ providedIn: 'root' })
  • Inject: private svc = inject(MyService) in a constructor or field
  • Generate: ng generate component foo, ng generate service foo, ng generate directive foo
Rune AI

Rune AI

Key Insights

  • Components are TypeScript classes plus a template plus scoped styles; use standalone: true in 2026.
  • Directives modify existing elements; structural directives are largely replaced by @if / @for blocks.
  • Services hold logic and data; mark them @Injectable({ providedIn: 'root' }) and inject with inject().
  • Use signal(), computed(), and effect() for reactivity instead of subscribing to RxJS by hand.
  • Components compose with input() for data in, output() for events out — the entire data-flow contract.
RunePowered by Rune AI

Frequently Asked Questions

Should I still use `*ngIf` and `*ngFor`?

For new code, no — use `@if` and `@for`. They are faster, more readable, and the framework default since Angular 17.

When should I write a directive vs a component?

Directive when you only modify an existing element (focus, tooltip, drag handle). Component when you have your own template.

Where should business logic live?

In services. Components should mostly orchestrate UI; services should hold the rules, the data, and the API calls.

Do I need to write `constructor(private foo: FooService)` anymore?

Either style works, but the `inject()` function is the modern recommendation — it composes better with mixins and works outside the constructor.

Can a service depend on another service?

Yes — inject one service into another the same way. Keep the dependency graph shallow and avoid cycles.

Conclusion

Components show, directives modify, services know. Build a small Angular app with one of each — a search component, an autofocus directive, a search-API service — and the framework's structure stops feeling like ceremony and starts feeling like guard rails. The patterns are intentionally repetitive, which is exactly why Angular scales to teams of fifty.