Michael Lo

Command Palette

Search for a command to run...

Blog
PreviousNext

重新認識 Angular - 專案建立與 Component

--

Angular 20 學習筆記,從專案建立到 Component 基礎

最近開始學習 Angular 20,這是我的學習筆記,會用 React 的概念對照,幫助自己理解。

Angular 20 和我印象中的差異

我對 Angular 的印象還停留在 NgModule、繁瑣的 decorator、Zone.js 的「魔法」,實際接觸 Angular 20 後發現變化非常大:

舊 AngularAngular 20引入版本
NgModule 必須Standalone Components(預設)v17
*ngIf, *ngFor@if, @for Control Flowv17
@Input() decoratorinput() functionv17.1
RxJS everywhereSignals 響應式系統v16 preview → v20 stable
Zone.js 偵測變更Zonelessv18 preview → v20.2 stable
Webpackesbuild + Vitev17
Karma 測試Web Test Runner / Vitestv20 實驗性

這篇用 React 概念對照 Angular 20,很多特性從 v17 就開始引入,v20 才全部穩定


專案建立

bash
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

Angular 預設 --standalone,不會產生 app.module.ts

專案結構對照

React (Vite)
src
components
Button.tsx
hooks
useAuth.ts
App.tsx
main.tsx
package.json
vite.config.ts
Angular
src
app
components
button
button.component.ts
services
auth.service.ts
app.component.ts
app.routes.ts
main.ts
angular.json
package.json

Angular 習慣用資料夾包 component,但 standalone 也可以像 React 一樣單檔案

第一個 Component

tsx
// Button.tsx
export function Button({ label }: { label: string }) {
  return <button>{label}</button>;
}

Angular Component 解析:

屬性說明
selectorHTML tag 名稱,如 <app-button>
standalone: true不需要 NgModule
template內嵌 HTML template
input()接收父層傳入的資料(類似 props)

Component 基礎

Template Syntax 對照

功能React JSXAngular Template
顯示變數{count}{{ count() }}
屬性綁定disabled={true}[disabled]="true"
事件綁定onClick={handler}(click)="handler()"
Class 綁定className={styles}[class]="styles"
Style 綁定style={{ color: 'red' }}[style.color]="'red'"

() = 事件輸出、[] = 資料輸入、[()] = 雙向綁定

Control Flow

Angular 17 引入新的 Control Flow 語法,取代 *ngIf / *ngFor

條件渲染

tsx
function Profile({ user }) {
  return (
    <div>
      {user ? (
        <span>Welcome, {user.name}</span>
      ) : (
        <span>Please login</span>
      )}
    </div>
  );
}

列表渲染

tsx
function UserList({ users }) {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

@for 內建 @empty 處理空陣列,track 取代舊的 trackBy function,簡潔很多

Switch 條件

tsx
function StatusBadge({ status }) {
  switch (status) {
    case 'active':
      return <span className="green">Active</span>;
    case 'pending':
      return <span className="yellow">Pending</span>;
    default:
      return <span className="gray">Unknown</span>;
  }
}

Component 溝通

Props (Input)

tsx
type Props = {
  title: string;
  count?: number;
};
 
function Card({ title, count = 0 }: Props) {
  return (
    <div>
      <h2>{title}</h2>
      <span>{count}</span>
    </div>
  );
}
 
// Usage
<Card title="Hello" count={5} />

Events (Output)

tsx
type Props = {
  onSelect: (id: string) => void;
};
 
function Item({ onSelect }: Props) {
  return (
    <button onClick={() => onSelect('123')}>
      Select
    </button>
  );
}
 
// Usage
<Item onSelect={(id) => console.log(id)} />

Two-way Binding (model)

model() 簡化雙向綁定(v17.2 引入)

typescript
// toggle.component.ts
@Component({
  selector: 'app-toggle',
  standalone: true,
  template: `
    <input
      type="checkbox"
      [checked]="checked()"
      (change)="checked.set($event.target.checked)"
    />
  `,
})
export class ToggleComponent {
  checked = model<boolean>(false);
}
 
// Usage: <app-toggle [(checked)]="isEnabled" />

model() 讓你用 [()] 語法雙向綁定,不用自己配 input + output

Content Projection

tsx
function Card({ children, header }) {
  return (
    <div className="card">
      <div className="card-header">{header}</div>
      <div className="card-body">{children}</div>
    </div>
  );
}
 
// Usage
 
<Card header={<h2>Title</h2>}>
  <p>Content here</p>
</Card>

常見問題

standalone: true 是什麼?還需要 NgModule 嗎?

Angular 17 開始 standalone 是預設值,component 不用宣告在 NgModule 裡,直接 import 就能用。舊專案的 NgModule 還是支援,但新專案建議全部 standalone

為什麼 template 要用 ()[]

語法意思記法
[property]資料輸入方括號 = 資料「進去」
(event)事件輸出小括號 = 事件「出來」
[()]雙向綁定結合 [] 輸入和 () 輸出

一眼就能看出資料流向

input() vs @Input() 差在哪?

input() 是新 API(v17.1),回傳 Signal:

  • 型別推斷更好
  • input.required<T>() 強制必填
  • 和 Signals 整合

@Input() 是舊語法,還能用但新專案建議用 input()


系列文章

  1. 專案建立與 Component ← 目前位置
  2. Signals 響應式狀態管理
  3. 路由、資料與表單
  4. 效能優化與部署