05_核心语法:模板与组件基础

核心语法:模板与组件基础

模板与组件是Angular应用的核心构建块。Angular v20引入了全新的模板语法和组件特性,旨在提升开发效率与应用性能。本章将详细讲解这些新特性,包括现代模板语法、组件的核心构成要素、指令系统以及管道的使用,帮助你建立Angular开发的基础认知。

1. 全新模板语法(v20推荐标准)

Angular v20推出了基于@符号的新一代模板语法,替代了传统的结构指令(如*ngIf*ngFor)。新语法不仅更简洁易读,还带来了更好的类型安全性和性能优化。

条件渲染:@if替代*ngIf,类型收窄与空值处理

@if指令用于根据条件渲染模板内容,相比传统的*ngIf,它提供了更直观的语法和更严格的类型检查。

基本用法

<!-- 新语法 -->
@if (isLoggedIn) {
  <p>欢迎回来,用户!</p>
}

<!-- 旧语法对比 -->
<p *ngIf="isLoggedIn">欢迎回来,用户!</p>

else分支处理

@if (user) {
  <p>用户名:{{ user.name }}</p>
} @else {
  <p>请登录</p>
}

@if的显著优势是自动类型收窄,当检查对象是否存在时,Angular模板会自动识别对象内部属性的可用性:

@if (user) {
  <!-- 此处TypeScript已知user不为null/undefined -->
  <p>年龄:{{ user.age }}</p>
} @else {
  <p>用户信息不存在</p>
}

循环渲染:@for与@empty空状态

@for指令用于遍历集合数据并渲染列表,替代了传统的*ngFor,强制要求指定跟踪标识(track)以优化性能。

基本用法

@for (product of products; track product.id) {
  <div class="product">
    <h3>{{ product.name }}</h3>
    <p>价格:{{ product.price }}</p>
  </div>
}

循环变量

@for (item of items; track item.id; let i = $index; let first = $first) {
  <div class="item {{ first ? 'first' : '' }}">
    第{{ i + 1 }}项:{{ item.name }}
  </div>
}

空状态处理

@for (message of messages; track message.id) {
  <div class="message">{{ message.content }}</div>
} @empty {
  <div class="no-messages">暂无消息</div>
}

变量声明:@let简化深层属性访问与异步数据订阅

@let指令允许在模板中声明变量,特别适合处理深层嵌套属性和异步数据:

@let fullName = user?.firstName + ' ' + user?.lastName;
<p>全名:{{ fullName }}</p>

<!-- 处理异步数据 -->
@let user = user$ | async;
@if (user) {
  <p>姓名:{{ user.name }}</p>
}

2. 指令系统:扩展模板功能

指令是Angular中用于扩展HTML元素功能的特殊类,分为属性指令(修改元素属性)和结构指令(修改DOM结构)两类。

2.1 内置指令

Angular提供了多个常用内置指令:

属性指令

  • ngClass:动态添加CSS类

    <div [ngClass]="{ 'active': isActive, 'disabled': isDisabled }"></div>
    
  • ngStyle:动态设置样式

    <p [ngStyle]="{ 'color': textColor, 'font-size': fontSize + 'px' }"></p>
    
  • ngModel:表单双向绑定(需导入FormsModule)

    <input type="text" [(ngModel)]="username">
    

结构指令
除了前面介绍的@if@for,还有@switch用于多条件分支:

@switch (status) {
  @case ('success') {
    <p class="success">操作成功</p>
  }
  @case ('error') {
    <p class="error">操作失败</p>
  }
  @default {
    <p>等待操作</p>
  }
}

2.2 自定义指令

创建自定义指令可以封装可复用的DOM操作逻辑。

创建属性指令

import { Directive, ElementRef, Input, OnInit } from '@angular/core';

@Directive({
  selector: '[appHighlight]',
  standalone: true
})
export class HighlightDirective implements OnInit {
  // 接收输入参数
  @Input() highlightColor = 'yellow';
  
  constructor(private el: ElementRef) {}
  
  ngOnInit() {
    // 修改宿主元素样式
    this.el.nativeElement.style.backgroundColor = this.highlightColor;
  }
}

使用自定义指令

<!-- 在模板中使用 -->
<p appHighlight>默认黄色高亮</p>
<p appHighlight [highlightColor]="'lightblue'">蓝色高亮</p>

与Signals结合

import { Directive, ElementRef, effect } from '@angular/core';
import { signal } from '@angular/core';

@Directive({
  selector: '[appScrollSpy]',
  standalone: true
})
export class ScrollSpyDirective {
  scrollPosition = signal(0);
  
  constructor(private el: ElementRef) {
    // 监听滚动事件
    window.addEventListener('scroll', () => {
      this.scrollPosition.set(window.scrollY);
    });
    
    // 响应式更新样式
    effect(() => {
      if (this.scrollPosition() > 100) {
        this.el.nativeElement.classList.add('scrolled');
      } else {
        this.el.nativeElement.classList.remove('scrolled');
      }
    });
  }
}

3. 管道:数据转换与格式化

管道(Pipe)用于在模板中转换数据格式,Angular提供了多种内置管道,并支持创建自定义管道。

3.1 内置管道

常用内置管道:

  • DatePipe:日期格式化

    <p>当前时间:{{ today | date:'yyyy-MM-dd HH:mm:ss' }}</p>
    
  • UpperCasePipe/LowerCasePipe:大小写转换

    <p>{{ 'hello' | uppercase }}</p> <!-- 输出 HELLO -->
    
  • CurrencyPipe:货币格式化

    <p>{{ price | currency:'CNY':'symbol' }}</p> <!-- 输出 ¥100.00 -->
    
  • AsyncPipe:处理异步数据(自动订阅和取消订阅)

    <p>{{ user$ | async | json }}</p>
    

3.2 自定义管道

创建自定义管道实现特定数据转换逻辑:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'truncate',
  standalone: true,
  pure: true // 纯管道,仅在输入变化时重新计算
})
export class TruncatePipe implements PipeTransform {
  // value: 输入值,args: 额外参数
  transform(value: string, maxLength: number = 10, suffix: string = '...'): string {
    if (value.length <= maxLength) return value;
    return value.slice(0, maxLength) + suffix;
  }
}

使用自定义管道

<!-- 在模板中使用 -->
<p>{{ longText | truncate:20:'...查看更多' }}</p>

<!-- 与其他管道结合使用 -->
<p>{{ longText | truncate:10 | uppercase }}</p>

非纯管道
非纯管道(pure: false)会在每次变更检测时重新计算,适合依赖外部状态的数据转换:

@Pipe({
  name: 'localTime',
  standalone: true,
  pure: false // 非纯管道
})
export class LocalTimePipe implements PipeTransform {
  transform(timestamp: number): string {
    return new Date(timestamp).toLocaleTimeString();
  }
}

4. 组件核心构成

组件是Angular应用的基本构建单元,每个组件由模板(Template)、类(Class)和样式(Styles)三部分组成。

4.1 装饰器配置:@Component元数据与独立组件

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HighlightDirective } from './highlight.directive';
import { TruncatePipe } from './truncate.pipe';

@Component({
  selector: 'app-user-profile',
  standalone: true,
  imports: [CommonModule, HighlightDirective, TruncatePipe], // 导入依赖
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.scss']
})
export class UserProfileComponent {
  // 组件逻辑
}

4.2 响应式数据:Signal创建与更新

import { Component, signal, computed } from '@angular/core';

@Component({
  selector: 'app-cart',
  standalone: true,
  template: `
    <p>商品总数:{{ totalItems() }}</p>
    <p>总价:{{ totalPrice() | currency }}</p>
  `
})
export class CartComponent {
  items = signal([
    { name: '笔记本', price: 5999, quantity: 1 }
  ]);
  
  // 计算属性
  totalItems = computed(() => {
    return this.items().reduce((sum, item) => sum + item.quantity, 0);
  });
  
  totalPrice = computed(() => {
    return this.items().reduce((sum, item) => sum + (item.price * item.quantity), 0);
  });
}

4.3 生命周期钩子:从ngOnInit到ngOnDestroy

import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { interval, Subscription } from 'rxjs';

@Component({
  selector: 'app-timer',
  standalone: true,
  template: `<p>已运行:{{ seconds }}秒</p>`
})
export class TimerComponent implements OnInit, OnDestroy {
  @Input() initialSeconds = 0;
  seconds = 0;
  private timerSubscription?: Subscription;
  
  ngOnInit() {
    this.seconds = this.initialSeconds;
    this.timerSubscription = interval(1000).subscribe(() => {
      this.seconds++;
    });
  }
  
  ngOnDestroy() {
    this.timerSubscription?.unsubscribe();
  }
}

通过本章学习,你已经掌握了Angular v20的核心模板语法、指令系统、管道使用以及组件基础。

posted @ 2025-09-21 18:19  S&L·chuck  阅读(12)  评论(0)    收藏  举报