第一个Angular应用 #
一、应用概述 #
我们将创建一个简单的待办事项(Todo)应用,学习Angular的核心概念:
- 组件创建
- 数据绑定
- 事件处理
- 条件渲染
- 列表渲染
二、创建项目 #
bash
ng new todo-app --style=css --routing=false --skip-tests
cd todo-app
ng serve
三、创建Todo组件 #
3.1 生成组件 #
bash
ng g c todo
3.2 定义数据模型 #
创建 src/app/todo/todo.model.ts:
typescript
export interface Todo {
id: number;
title: string;
completed: boolean;
createdAt: Date;
}
3.3 组件逻辑 #
编辑 src/app/todo/todo.component.ts:
typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Todo } from './todo.model';
@Component({
selector: 'app-todo',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './todo.component.html',
styleUrl: './todo.component.css'
})
export class TodoComponent {
todos: Todo[] = [];
newTodoTitle: string = '';
nextId: number = 1;
addTodo() {
if (this.newTodoTitle.trim()) {
const newTodo: Todo = {
id: this.nextId++,
title: this.newTodoTitle.trim(),
completed: false,
createdAt: new Date()
};
this.todos.push(newTodo);
this.newTodoTitle = '';
}
}
toggleTodo(todo: Todo) {
todo.completed = !todo.completed;
}
deleteTodo(id: number) {
this.todos = this.todos.filter(todo => todo.id !== id);
}
get completedCount(): number {
return this.todos.filter(todo => todo.completed).length;
}
get totalCount(): number {
return this.todos.length;
}
}
3.4 组件模板 #
编辑 src/app/todo/todo.component.html:
html
<div class="todo-container">
<h1>待办事项</h1>
<div class="todo-input">
<input
type="text"
[(ngModel)]="newTodoTitle"
placeholder="添加新任务..."
(keyup.enter)="addTodo()"
/>
<button (click)="addTodo()">添加</button>
</div>
<div class="todo-stats">
<span>总计: {{ totalCount }}</span>
<span>已完成: {{ completedCount }}</span>
</div>
<ul class="todo-list">
<li *ngFor="let todo of todos" [class.completed]="todo.completed">
<input
type="checkbox"
[checked]="todo.completed"
(change)="toggleTodo(todo)"
/>
<span>{{ todo.title }}</span>
<button (click)="deleteTodo(todo.id)">删除</button>
</li>
</ul>
<div *ngIf="todos.length === 0" class="empty-message">
暂无待办事项,添加一个吧!
</div>
</div>
3.5 组件样式 #
编辑 src/app/todo/todo.component.css:
css
.todo-container {
max-width: 500px;
margin: 50px auto;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
color: #333;
}
.todo-input {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.todo-input input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.todo-input button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.todo-input button:hover {
background-color: #45a049;
}
.todo-stats {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
color: #666;
}
.todo-list {
list-style: none;
padding: 0;
}
.todo-list li {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.todo-list li.completed span {
text-decoration: line-through;
color: #999;
}
.todo-list li span {
flex: 1;
margin: 0 10px;
}
.todo-list li button {
padding: 5px 10px;
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.empty-message {
text-align: center;
color: #999;
padding: 20px;
}
四、更新AppComponent #
编辑 src/app/app.component.ts:
typescript
import { Component } from '@angular/core';
import { TodoComponent } from './todo/todo.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [TodoComponent],
template: `
<app-todo></app-todo>
`,
styles: []
})
export class AppComponent {}
五、核心概念解析 #
5.1 组件结构 #
text
TodoComponent
├── @Component装饰器 定义组件元数据
├── selector 组件选择器
├── standalone 独立组件标志
├── imports 导入依赖模块
├── templateUrl 模板文件路径
└── styleUrl 样式文件路径
5.2 数据绑定 #
html
<!-- 插值绑定 -->
<span>{{ todo.title }}</span>
<!-- 属性绑定 -->
<input [checked]="todo.completed" />
<!-- 事件绑定 -->
<button (click)="deleteTodo(todo.id)">删除</button>
<!-- 双向绑定 -->
<input [(ngModel)]="newTodoTitle" />
5.3 指令使用 #
html
<!-- *ngFor 列表渲染 -->
<li *ngFor="let todo of todos"></li>
<!-- *ngIf 条件渲染 -->
<div *ngIf="todos.length === 0"></div>
<!-- [class] 类绑定 -->
<li [class.completed]="todo.completed"></li>
六、数据绑定详解 #
6.1 插值表达式 #
html
<p>{{ totalCount }}</p>
<p>{{ 1 + 1 }}</p>
<p>{{ 'Hello' + ' Angular' }}</p>
<p>{{ getCompletedCount() }}</p>
6.2 属性绑定 #
html
<!-- 绑定属性 -->
<img [src]="imageUrl" />
<a [href]="linkUrl">链接</a>
<button [disabled]="isDisabled">按钮</button>
<!-- 绑定样式 -->
<div [style.color]="'red'">红色文字</div>
<div [style.fontSize.px]="16">16px字体</div>
<!-- 绑定类 -->
<div [class.active]="isActive">激活状态</div>
<div [class]="{'active': isActive, 'disabled': isDisabled}">多类绑定</div>
6.3 事件绑定 #
html
<!-- 点击事件 -->
<button (click)="addTodo()">添加</button>
<!-- 键盘事件 -->
<input (keyup.enter)="addTodo()" />
<input (keyup.escape)="clearInput()" />
<!-- 传递事件对象 -->
<input (input)="onInput($event)" />
<!-- 传递参数 -->
<button (click)="deleteTodo(todo.id)">删除</button>
6.4 双向绑定 #
html
<!-- 使用ngModel需要导入FormsModule -->
<input [(ngModel)]="newTodoTitle" />
<!-- 等价于 -->
<input
[value]="newTodoTitle"
(input)="newTodoTitle = $event.target.value"
/>
七、内置指令 #
7.1 *ngIf #
html
<!-- 基本用法 -->
<div *ngIf="isVisible">显示内容</div>
<!-- else分支 -->
<div *ngIf="isLoading; else loaded">
加载中...
</div>
<ng-template #loaded>
加载完成
</ng-template>
7.2 *ngFor #
html
<!-- 基本用法 -->
<li *ngFor="let todo of todos">{{ todo.title }}</li>
<!-- 获取索引 -->
<li *ngFor="let todo of todos; let i = index">
{{ i + 1 }}. {{ todo.title }}
</li>
<!-- 其他变量 -->
<li *ngFor="let todo of todos;
let i = index;
let first = first;
let last = last;
let even = even;
let odd = odd">
{{ i }} - {{ todo.title }}
</li>
<!-- trackBy优化性能 -->
<li *ngFor="let todo of todos; trackBy: trackByFn">
{{ todo.title }}
</li>
typescript
trackByFn(index: number, todo: Todo): number {
return todo.id;
}
7.3 *ngSwitch #
html
<div [ngSwitch]="status">
<p *ngSwitchCase="'active'">激活状态</p>
<p *ngSwitchCase="'inactive'">未激活状态</p>
<p *ngSwitchDefault>未知状态</p>
</div>
八、管道(Pipes) #
8.1 内置管道 #
html
<!-- 日期管道 -->
<p>{{ todo.createdAt | date }}</p>
<p>{{ todo.createdAt | date:'yyyy-MM-dd' }}</p>
<p>{{ todo.createdAt | date:'fullDate' }}</p>
<!-- 大小写管道 -->
<p>{{ title | uppercase }}</p>
<p>{{ title | lowercase }}</p>
<p>{{ title | titlecase }}</p>
<!-- 数字管道 -->
<p>{{ price | number }}</p>
<p>{{ price | number:'1.2-2' }}</p>
<p>{{ price | currency:'CNY' }}</p>
<!-- 百分比管道 -->
<p>{{ progress | percent }}</p>
<!-- JSON管道(调试用) -->
<pre>{{ todo | json }}</pre>
8.2 链式管道 #
html
<p>{{ todo.title | uppercase | slice:0:10 }}</p>
九、应用效果 #
text
┌─────────────────────────────────────┐
│ 待办事项 │
├─────────────────────────────────────┤
│ [添加新任务... ] [添加] │
├─────────────────────────────────────┤
│ 总计: 3 已完成: 1 │
├─────────────────────────────────────┤
│ ☐ 学习Angular基础 [删除] │
│ ☑ 完成第一个应用 [删除] │
│ ☐ 学习路由 [删除] │
└─────────────────────────────────────┘
十、总结 #
本章节我们学习了:
| 概念 | 说明 |
|---|---|
| 组件 | Angular应用的基本构建块 |
| 数据绑定 | 组件与模板之间的数据交互 |
| 事件绑定 | 处理用户交互 |
| 指令 | 扩展HTML功能 |
| 管道 | 数据格式化转换 |
下一步:Angular项目结构
最后更新:2026-03-26