C

Components

Type-safe Angular components with proper Input/Output decorators and lifecycle hooks.

Code

typescript
1// user-card.component.ts
2import {
3 Component,
4 Input,
5 Output,
6 EventEmitter,
7 OnInit,
8 OnChanges,
9 SimpleChanges,
10 ChangeDetectionStrategy,
11} from "@angular/core";
12
13// Interface for component data
14interface User {
15 id: number;
16 name: string;
17 email: string;
18 avatar?: string;
19 role: "admin" | "user" | "guest";
20}
21
22interface UserAction {
23 type: "edit" | "delete" | "view";
24 user: User;
25}
26
27@Component({
28 selector: "app-user-card",
29 standalone: true,
30 changeDetection: ChangeDetectionStrategy.OnPush,
31 template: `
32 <div class="user-card" [class.admin]="user.role === 'admin'">
33 <img [src]="user.avatar || defaultAvatar" [alt]="user.name" />
34 <h3>{{ user.name }}</h3>
35 <p>{{ user.email }}</p>
36 <span class="role-badge">{{ user.role }}</span>
37
38 <div class="actions">
39 <button (click)="onAction('view')">View</button>
40 <button (click)="onAction('edit')" *ngIf="editable">Edit</button>
41 <button (click)="onAction('delete')" *ngIf="deletable">Delete</button>
42 </div>
43 </div>
44 `,
45 styles: [`
46 .user-card { padding: 1rem; border: 1px solid #ddd; border-radius: 8px; }
47 .admin { border-color: #3b82f6; }
48 .role-badge { padding: 2px 8px; border-radius: 4px; background: #e5e7eb; }
49 `],
50})
51export class UserCardComponent implements OnInit, OnChanges {
52 // Input properties with default values
53 @Input({ required: true }) user!: User;
54 @Input() editable = false;
55 @Input() deletable = false;
56 @Input() defaultAvatar = "/assets/default-avatar.png";
57
58 // Output events
59 @Output() action = new EventEmitter<UserAction>();
60 @Output() userChange = new EventEmitter<User>();
61
62 ngOnInit(): void {
63 console.log("UserCard initialized for:", this.user.name);
64 }
65
66 ngOnChanges(changes: SimpleChanges): void {
67 if (changes["user"]) {
68 console.log(
69 "User changed from",
70 changes["user"].previousValue,
71 "to",
72 changes["user"].currentValue
73 );
74 }
75 }
76
77 onAction(type: UserAction["type"]): void {
78 this.action.emit({ type, user: this.user });
79 }
80}
81
82// user-list.component.ts - Parent component
83@Component({
84 selector: "app-user-list",
85 standalone: true,
86 imports: [UserCardComponent],
87 template: `
88 <div class="user-list">
89 <h2>Users ({{ users.length }})</h2>
90 @for (user of users; track user.id) {
91 <app-user-card
92 [user]="user"
93 [editable]="canEdit"
94 [deletable]="canDelete"
95 (action)="handleAction($event)"
96 />
97 }
98 </div>
99 `,
100})
101export class UserListComponent {
102 users: User[] = [
103 { id: 1, name: "Alice", email: "alice@example.com", role: "admin" },
104 { id: 2, name: "Bob", email: "bob@example.com", role: "user" },
105 ];
106
107 canEdit = true;
108 canDelete = false;
109
110 handleAction(event: UserAction): void {
111 console.log(`Action: ${event.type} on user: ${event.user.name}`);
112
113 switch (event.type) {
114 case "view":
115 // Navigate to user detail
116 break;
117 case "edit":
118 // Open edit modal
119 break;
120 case "delete":
121 this.users = this.users.filter((u) => u.id !== event.user.id);
122 break;
123 }
124 }
125}

Run this example locally

$ ng new my-app --strict