已发布·持续改进
Angular 指南 · 3/6
本章目前仅提供英文版。
Angular applications are built from a handful of ideas: standalone components with templates, signals for reactive state, services shared through dependency injection, and the router. This chapter covers each of them with small, working examples.
A component is a TypeScript class with the @Component decorator. It declares a selector, a template and the other components or directives it uses in imports. Components are standalone by default, so no NgModule is needed. Inputs and outputs are declared with the input() and output() functions:
import { Component, input, output } from '@angular/core';
export interface Hero {
id: number;
name: string;
}
@Component({
selector: 'app-hero-card',
template: `
<h3>{{ hero().name }}</h3>
<button type="button" (click)="selected.emit(hero())">Select</button>
`,
})
export class HeroCard {
readonly hero = input.required<Hero>();
readonly selected = output<Hero>();
}An input is itself a signal, so the template reads it as hero(). A parent uses the component like an HTML element and must add HeroCard to its own imports.
Templates are HTML with a few extra syntaxes:
| Syntax | Meaning |
|---|---|
{{ value }} | Text interpolation |
[property]="expr" | Bind an element or component property |
(event)="handler($event)" | Listen to a DOM event or component output |
[(value)]="state" | Two-way binding (for example with model() inputs) |
[class.active]="isActive" | Toggle a CSS class |
Built-in control flow blocks replace the older *ngIf and *ngFor directives and need no imports:
<ul>
@for (hero of heroes(); track hero.id) {
<li>
<app-hero-card [hero]="hero" (selected)="select($event)" />
</li>
} @empty {
<li>No heroes yet.</li>
}
</ul>
@if (current(); as hero) {
<p>Selected: {{ hero.name }}</p>
} @else {
<p>Pick a hero.</p>
}
@switch (mode()) {
@case ('edit') { <p>Editing</p> }
@default { <p>Read-only view</p> }
}@for requires a track expression, usually a unique id, so Angular can reuse DOM nodes when the list changes. @empty renders when the list has no items. There is also for lazy-loading part of a template and for declaring a local template variable.
@defer@letA signal is a value that notifies Angular when it changes. computed() derives a read-only value from other signals and recalculates only when they change. effect() runs side effects, such as logging or syncing to storage, whenever the signals it reads change:
import { Component, computed, effect, signal } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<p>{{ count() }} x 2 = {{ double() }}</p>
<button type="button" (click)="increment()">+1</button>
`,
})
export class Counter {
protected readonly count = signal(0);
protected readonly double = computed(() => this.count() * 2);
constructor() {
effect(() => console.log('count is now', this.count()));
}
increment() {
this.count.update((value) => value + 1);
}
}Use set() to replace a value and update() to derive the next value from the current one. Prefer computed() over effect() for anything that can be expressed as a derived value.
Logic that several components share belongs in a service. @Injectable({ providedIn: 'root' }) makes one instance available to the whole app, and inject() retrieves it:
import { Injectable, signal } from '@angular/core';
import { Hero } from './hero-card';
@Injectable({ providedIn: 'root' })
export class HeroService {
private readonly heroes = signal<Hero[]>([{ id: 1, name: 'Windstorm' }]);
readonly all = this.heroes.asReadonly();
add(name: string) {
this.heroes.update((list) => [...list, { id: Date.now(), name }]);
}
}In a component, write private readonly heroService = inject(HeroService); as a class field. Angular creates the service the first time it is requested. To give a component its own instance instead, list the service in that component's providers array.
Routes map URL paths to components. loadComponent splits a page into a separate bundle that loads only when the route is visited:
import { Routes } from '@angular/router';
import { HeroList } from './heroes/hero-list';
export const routes: Routes = [
{ path: '', component: HeroList, title: 'Heroes' },
{
path: 'heroes/:id',
loadComponent: () => import('./heroes/hero-detail').then((m) => m.HeroDetail),
},
{ path: '**', redirectTo: '' },
];Import RouterLink and RouterOutlet where you need them, link with <a routerLink="/heroes/42">, and render the active page with <router-outlet />. With withComponentInputBinding() enabled in provideRouter, a route parameter such as id arrives in the component as readonly id = input<string>(). Guards such as canActivate are plain functions that can inject() services.
imports.input(), output() and template bindings connect parent and child components.@if, @for (with track) and @switch control what the template renders.computed() and effect() hold and react to state.providedIn: 'root' are shared through inject(); the router maps URLs to components.
0 条评论
登录 · 登录后即可发表评论。
来发表第一条评论吧。