昨天,本來信誓旦旦地說:
該來跟前端培養下感情了吧!
然後前端一行都沒寫 hhh。
反而一路從登入頁,挖回 Account 的 Identity、Partial Unique Index,最後甚至又生了一個 Alembic Migration 出來。
只能說,有些感情還沒開始,就得先處理上一段感情留下來的問題。
不過小問題。
今天不一樣。
今天沒有資料庫。
沒有 Migration。
沒有 Partial Unique Index。
更沒有五天前的蘇某突然從土裡爬出來,告訴我:
「欸嘿,其實這裡還有一顆雷喔。」
你確定?
……
你不要烏鴉嘴。
總之,今天真的要來寫前端了。
既然都要開始寫前端了,那第一個問題自然是:
用什麼?
其實這題我倒沒有糾結多久。
React,我用過。
Angular,我也用過。
兩個要拿來做這次的系統,基本上都沒什麼問題。
所以按照正常的技術文章流程,這裡應該放上一張表格,從生態系、效能、學習曲線、開發體驗一路比較,最後經過一番嚴謹的分析——
所以你選哪個?
Angular。
為什麼?
想用。
蛤?
就,想用。
沒了。
倒也不是 React 做不到什麼,也不是 Angular 在這個專案有什麼壓倒性的優勢。
單純就是兩個我都碰過,兩個也都足以應付這次的需求。
既然如此,那就挑個想寫的。
畢竟技術選型雖然需要考慮需求,也不代表每一次都得寫一大篇。
所以——
Angular,啟動!
今天會是 ChatGPT 配 Antigravity,會這麼做的原因是:我下午把 Codex 額度用完了。
總之,先來初始化專案吧。
ng new frontend
stylesheet 我選的是 SCSS,純個人偏好。
SSR 跟 SSG 這次我認為不需要採用。
至於 AI tools,說真的我自己用下來沒什麼特別的感覺,這次就先選 None 吧。
好,專案生出來了。
所以現在開始寫登入頁?
先等等。
畢竟這是這系列第一次正式碰 Angular,至少先看看它到底生了什麼東西出來。
frontend
├── public
│ └── favicon.ico
├── src
│ ├── app
│ │ ├── app.config.ts
│ │ ├── app.html
│ │ ├── app.routes.ts
│ │ ├── app.scss
│ │ ├── app.spec.ts
│ │ └── app.ts
│ ├── index.html
│ ├── main.ts
│ └── styles.scss
├── angular.json
├── package-lock.json
├── package.json
├── README.md
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
先 serve 起來看看吧。

熟悉的歡迎頁。
好,看到了,可以刪了。
確實。
不過在把它拆掉以前,先看看剛才 Angular CLI 到底幫我們生了些什麼。
如果有用過比較早期的 Angular,看到這份目錄可能會先發現一件事。
等等,我的 app.module.ts 呢?
被你刪了?
我才剛建立專案是能刪什麼啦。
以前寫 Angular 時,很常會看到這種東西:
app.module.ts
然後 Component、Service,以及其他需要使用的東西,會圍繞著 NgModule 組織起來。
但我這次透過 Angular CLI 建立的專案,預設採用了 Standalone 的方式。
所以我們現在看到的:
app.ts
app.html
app.scss
app.config.ts
app.routes.ts
就是接下來主要會碰到的幾個東西。
先不用急著把每一個都研究一遍。
今天的目標只有一個:
把這個歡迎頁拆掉,換成我們自己的登入頁。
終於。
對,終於。
Angular CLI 都裝了,這種事情當然不用自己慢慢建檔案。
ng generate component pages/login
或者偷懶一點:
ng g c pages/login
所以
g是?
generate。
c呢?
component。
那你多打幾個字是會怎樣?
會累。
總之,執行完之後,Angular CLI 就會幫我們把 Login Component 的基本結構建起來。
接下來終於可以把昨天欠下來的登入頁畫出來了。
先把登入頁面寫出來,等等 API 再叫 Agent 接。
先把 Login 頁面接上 route 吧。
等等,為什麼有奇怪的 margin?

也沒有地方有寫 css 啊?
不對,我想起來了,瀏覽器有 User Agent Stylesheet,會替 body、標題、段落等元素提供預設樣式。現在看到的外圍空白,多半就是 body 的預設 margin。
那這邊就來一手 CSS Reset 吧。
CSS Reset?
簡單來說,不同 HTML 元素本身就會帶有瀏覽器提供的預設樣式。
像剛才看到的 body 外圍空白,就是其中一個例子;標題、段落、列表等等,也都有各自的預設 margin、padding 或其他樣式。
這些預設樣式本身不是什麼壞東西,但既然現在要自己控制整個頁面的樣式,我會希望大家先站在比較一致的起跑點。
所以 CSS Reset 做的事情,就是先把這些預設樣式清掉,再由我們自己決定它們應該長什麼樣子。
這邊我使用的是 Eric Meyer 的 Reset CSS。
把內容放進全域的 styles.scss 後,再回來看看。

欸,白邊沒了。
舒服了。
白邊處理完了,總算可以開始處理正事。
先來回憶一下昨天到底折騰了些什麼。
學生登入需要三個東西:
帳號
職務
密碼
老師則是:
帳號
密碼
這不是昨天才講過?
對,但昨天它們還只是後端裡面的資料。
今天這個差異終於要真的長到使用者面前了。
既然學生跟老師需要填的東西不一樣,那登入頁首先得讓使用者告訴我們:
你是學生,還是老師?
我打算先做成兩個可以切換的選項。
選學生時:
┌─────────────────────┐
│ 登入 │
│ │
│ [ 學生 ] [ 教師 ] │
│ │
│ 帳號 │
│ [ ] │
│ │
│ 職務 │
│ [ ] │
│ │
│ 密碼 │
│ [ ] │
│ │
│ [ 登入 ] │
└─────────────────────┘
切到教師:
┌─────────────────────┐
│ 登入 │
│ │
│ [ 學生 ] [ 教師 ] │
│ │
│ 帳號 │
│ [ ] │
│ │
│ 密碼 │
│ [ ] │
│ │
│ [ 登入 ] │
└─────────────────────┘
差別就只有那個「職務」。
看起來很簡單。
你昨天也是這麼說的。
……
謝謝你歐。
總之先來寫吧。
UI 交給我,API 交給 Agent。

這個頁面整體看下來,我還是挺滿意的。
但有個地方我想改一下。

(麻煩各位想像一下在左右圖中切換的感覺)
畫面感覺一直在閃,好不舒服。
不錯,是不是覺得各個 UI 元件在螢幕上晃來晃去?
原因其實很簡單。
學生比教師多了一個「職務」欄位,所以切換身分時,整個表單的高度也會跟著改變。
偏偏我原本又把整份表單垂直置中。
於是表單高度一變,垂直置中的位置也跟著改變,最後連上面的帳號欄位都一起跑了。
但仔細想想,其實根本沒有必要讓所有東西一起動。
從學生切成教師,真正改變的只有「職務」這一欄。
那乾脆讓整份表單以上方為基準往下排。
這樣切換時,上面的標題、身分選擇、帳號都不用動;職務消失之後,只有下面的密碼和登入按鈕往上補。

歐,舒服多了。
對嘛,能少動就少動。
還有一些細節可以調整,但就不這麼吹毛求疵吧。
好,今天就先做到這裡吧。
至少昨天欠下來的前端,今天是真的寫了。
所以可以登入了嗎?
……
不行。
那你今天到底——
UI 寫完了嘛!
API 明天再接。
你昨天也是說明天。
閉嘴。
總之,今天至少成功把 Angular 專案架起來,也把登入頁的基本 Layout 和學生、教師兩種登入狀態處理好了。
至於現在這顆「登入」按鈕……
它目前最大的功能,就是被按。
明天再讓它真的做點事情。