###### tags: `Work`
`Try to work with the wind, don't fight it all the time`
# 前端開發 實務歷程
:::spoiler 目錄
[TOC]
:::
## 注意事項
### 改專案程式的前置作業
如果 有調整到前端共用目錄,造成程式上傳會出現衝突,所以跟冠州經理請教後,
需要從我這裡先推送一版,這樣你才能抓新的下來繼續開發
### ==要記得前端開發都先建目錄==
## 程式上傳流程
### 1.在終端機下指令 ==ng lint==
#### 黃色:警告 (視情況可被允許存在) // 紅色:錯誤
:::danger
> ng lint 要下在 ErpNext\NextFrontEnd 層
> ng lint 會針對檔案去看有無問題,其作用等同於查看網頁開發人員的 Web Error List
:::

### ==送正式的approve都不能自己按==

## 簡易執行
版本:Angular 12、Node V16.14.0、npm 8.3.1
先確認版本:ng version、npm -v,然後用nvm切換node
nvm切換:https://www.casper.tw/development/2022/01/10/install-nvm/
> ==SmartERP==
npm i @angular/cli@11.2.19
nvm use 16.20.2
> AppStore
npm i @angular/cli@14.2.13
nvm use 16.20.2
> ==REERP==
npm i @angular/cli
nvm use 20.16.0
> ==REERP、酒窖專案==
npm i @angular/cli
nvm use 16.14.0
PS.酒窖Angular 版本:16.2.1
酒窖

1. cd src/app
2. ng g m cfvInfoHome --routing 建專案(含module & routing)
-> 如果簡寫指令不行就完整指令:`ng g m CEIntensityReport --routing`
3. cd cfvInfoHome (專案資料夾名稱)
4. ng g s cfvInfoHome --skipTests 建 service
`ng generate service cfvInfoHome --skipTests`
5. ng g c container --skipTests
`ng generate container cfvInfoHome --skipTests`
6. ng g c index --skipTests
`c 代表 component` 所以 n g c 後面接的是要建立的component Name
※ 這邊要注意
前端 app-routing 其他人建目錄可能會與其他分支有開發上的衝突
==Component 名稱太長 => 路徑過長,發佈會出問題喔==
> 所以建好目錄(含含module & routing) 就要上傳一版正式
> 上傳名義:建立"專案名稱"目錄
> 注意要 <font style="color: #FFCC99">等跑完上傳正式</font>,再開要開發的分支,否則會沒有同步到程式目錄
※之後可能會遇到的問題
如果沒辦法一步到位建立完專案 & Component,那記得改完Component名字後,要先設定好module & routing,不然在引用其他Component的時候可能會無法引用
### 看文件注意事項
> ### 模組不是資料夾的意思
只是個分類(總之不用管)
看`資料夾名稱`就好,那就是第一層要建的資料夾
啊建的時後確認下建立的位置嘿

## 創建架構執行步驟 (幕僚--緯澤註)
1. 先產生Module主體(有routing)
```javascript
ng g m CFVInfoImport --routing=true
```
設定cfvinfo-import.routing.module初始會導到Container。
> 這邊要注意一個Component不能寫到兩個routing檔案
> 除非拉到共用後,然後在 Module用import引入,declarations啟用
```javascript
const routes: Routes = [{
path: '',
component: CFVInfoImportContainerComponent,
children: [
{
path: '',
redirectTo: '',
pathMatch: 'full'
}
]
}];
```
2. 再產生Container
```javascript
ng g c cfvinfo-import/CFVInfoImportContainer --skip-tests
```
Container容器的描述設定,可以刪除scss檔。設定styleUrls抓bootstrap.scss、material.scss。(可能會用到)
3. 產生Service服務
```javascript
ng g s cfvinfo-import/cfvinfoImport --skip-tests
```
providedIn: ‘root’ 要移除 因為他不是在根部
帶這個service給Cfvinfo_import.module認識 providers: [ CfvinfoImportService] (要先import引薦一下)
Service的*constructor*注入private *http*: **HttpClient**,並且import。
```
找一個專案複製相關需要的code
要用jsonServer的話,就把useJsonServer設成true
定義private apiPrefix = 'Erpnextfiapi/api/workItems/';
定義private subjectSampleData: BehaviorSubject<Sample[]> = new BehaviorSubject([]);
定義private GetSampleData = 'GetSampleData';
**ERP的API多是用POST,但是jsonServer要用get
run-JsonServer.bat
```
4. app-routing.module.ts增加路由
```javascript
{
path: 'COCA202S00',
loadChildren: () => import('./cfvinfo-import/cfvinfo-import.module')
.then((m) => m.CFVInfoImportModule),
},
```
測試撰寫的時候,會顯把其他路由先註解掉(approve上面的23~156行),以免run起來全部都跑,會很慢。
5. module 再改的時候,記得要將routing所在的component放入,否則可能會跑不出對應的前端component畫面

6. 執行 ng lint,看有沒有格式錯誤。
7. 執行 npm start
執行成功後,http://localhost:4200/ 會跳到ERP首頁,登入後,因為我們的程式是在A0A1使用的,直接把網址
http://localhost:4200/#/A0A1/smart-home
,後方的smart-home改成我們路由參數COCA202S00。
後續
2023/08/01 增加index,引用ag-grid。
## 問題集
### Expected linebreaks to be 'CRLF' but found 'LF'
這個問題通常出現在使用不同作業系統的開發者共同協作時,因為不同的作業系統使用不同的換行符號。Windows 使用 CRLF (Carriage Return + Line Feed) 作為換行符號,而 Unix 和 macOS 使用 LF (Line Feed)。
VSCode 是跨平台的程式碼編輯器,它會根據你的作業系統自動選擇換行符號,但在共同協作或使用版本控制工具時,這可能會引起問題。
要解決這個問題,你可以按照以下步驟進行調整:
1. 在 VSCode 中,點擊右下角的換行符號選項,它顯示為 "CRLF" 或 "LF"。
2. 點擊這個換行符號選項,它會彈出一個選單。
3. 選擇 "CRLF" 作為換行符號,這樣就能夠符合預期的格式。
完成以上步驟後,VSCode 就會按照預期的格式插入換行符號,不再出現 "Expected linebreaks to be 'CRLF' but found 'LF'" 的問題。如果你是在與他人共同協作的項目中,建議確認團隊中其他成員所使用的換行符號設置,以確保大家的編輯器設置一致。
### component命名
component不會有大寫,大寫會轉成底寫_。
ex.CFVInfoImport >> cfvinfo_import。(連續的大寫會判定自動小寫,駝峰的大寫會變成底線)
### 測試IT環境API
要改的地方:
environment.ts把production改成false。(不要遷入)
cfvinfo-import.service.ts裡面constructor補上判斷IT的url
```
const enableIT = true;
if (enableIT) {
this.apiPrefix = 'http://smartit.aurora.com.tw/auroraCarbonEmissions/api/CarbonEmissions/';
}
```
## 開發實務問題
### 跨 Service傳遞資料或參數,可能資料未傳遞完就完成跳頁,會造成訂閱判定無資料
情境:酒窖列表,藉由點擊庫存數量,切換到酒窖(分倉)報表
問題:但會出現報表內容重複的問題,看似資料有傳到,從頁面畫面來看會出現重複新增報表內容的狀況

從程式面來看,這是訂閱判定沒資料的結果
=> 酒窖列表的庫存連結點擊事件

=> 報表頁初始設定

原因:因為第一次資料還沒傳完,router就打斷,導致第二次點即會出現兩次傳遞的狀況

解法

### loseFocus 離開輸入框執行事件
以 input 來說 (blur) = "事件名稱()"
## ERP 程式目錄設定

通常選OA_TEST

進入目錄程式後,新增目錄程式
程式編號、程式描述填程式名稱,程式類型一般填Smart 程式(就是指新程式)

再次進入頁面後,查詢確認新增

進入目錄設定


放到對應位置

放哪裡通常問經理

## 什麼是變更檢測(Change Detection)?
變更檢測 是 Angular 應用程序的一個核心機制,用來確保應用的數據(如組件的屬性)和視圖(HTML 模板)保持同步。當數據發生變化時,Angular 會自動更新視圖來反映這些變化。
## ChangeDetectorRef 是什麼?
ChangeDetectorRef 是 Angular 提供的一個服務,讓你可以更細緻地控制變更檢測的行為。它允許你:
- 手動觸發變更檢測:
當你在 Angular 的自動變更檢測範圍之外(例如,使用第三方庫或原生 JavaScript 操作 DOM)修改數據時,可以使用 ChangeDetectorRef 來告訴 Angular 有數據變更需要更新視圖。
- 控制變更檢測的策略:
你可以暫停、啟動或標記組件為需要檢查,這對於優化性能或處理複雜的數據流非常有用。
## 何時使用 ChangeDetectorRef?
以下是一些常見的使用情境:
在非 Angular 事件中修改數據: 當你使用原生 JavaScript 或第三方庫(如 jQuery)來改變組件的數據時,Angular 無法自動檢測到這些變更。此時,你可以使用 ChangeDetectorRef 來手動觸發變更檢測。
解決 ExpressionChangedAfterItHasBeenCheckedError 錯誤: 當你在組件的生命周期鉤子(如 ngAfterViewInit)中更新數據,可能會觸發這個錯誤。使用 ChangeDetectorRef 可以幫助你在正確的時間點更新變更檢測。
優化性能: 在某些高性能需求的應用中,你可能希望精確控制變更檢測的執行,避免不必要的檢測循環。ChangeDetectorRef 提供的方法可以幫助你達到這個目標。
## 如何使用 ChangeDetectorRef?
```ts=
import { Component, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-example',
template: `
<div>
<p>{{ message }}</p>
<button (click)="updateMessage()">更新訊息</button>
</div>
`
})
export class ExampleComponent {
message: string = '初始訊息';
constructor(private cdr: ChangeDetectorRef) {}
updateMessage() {
setTimeout(() => {
this.message = '訊息已更新!';
// 手動觸發變更檢測
this.cdr.detectChanges();
}, 1000);
}
}
```
==detectChanges()==: 立即執行變更檢測,更新組件及其子組件的視圖。
```ts=
this.cdr.detectChanges();
```
==markForCheck()==: 標記組件為需要變更檢測,但不立即執行。適用於 OnPush 變更檢測策略。
```ts=
this.cdr.markForCheck();
```
==detach()==: 斷開組件的變更檢測,讓其不再自動檢測變更。
```ts=
this.cdr.detach();
```
==reattach()==: 重新連接組件的變更檢測,使其恢復自動檢測。
```ts=
this.cdr.reattach();
```
## 202410 網站更新 瀏覽器優化CSS
問題情境描述:
本機設計按鈕樣式 border-color: rgb(121, 121, 121);,更新網站後,
發現變成 border-color: #797979;
### 解
1. CSS Minification(CSS 壓縮): 當你部署或更新網站時,很多現代的構建工具(如 Webpack、Gulp 等)會對 CSS 進行壓縮和優化。這些工具通常會自動將 CSS 的值進行簡化,比如:
- rgb(255, 255, 255) 被簡化為 #fff,這是 CSS 中的標準簡寫。
- rgb(121, 121, 121) 被簡化為 #797979。
- 字體名稱中的 Unicode 字符可能會被轉換成 Unicode 表示法,比如 微軟正黑體 變成了 \5fae\8edf\6b63\9ed1\9ad4。
2. CSS 標準簡化:
- #fff 和 rgb(255, 255, 255) 表示相同的顏色(白色),使用十六進制表示法是一種更簡短且標準化的方式。
字體名稱中的中文字符會被工具或瀏覽器自動轉換為 Unicode 編碼,這是為了更好地支持跨瀏覽器的字體渲染。
- 構建工具或自動化流程的影響: 如果你使用了自動化構建工具來打包和優化你的項目(例如使用工具將 CSS、JavaScript 進行壓縮、合併),它們可能會對 CSS 文件進行壓縮。這是一種性能優化,可以減少文件大小,加快加載速度。
總結:
簡單來說,==並不會改變實際的樣式效果==
可以理解為瀏覽器在自動優化或處理 CSS 代碼時的行為。現代瀏覽器在渲染頁面時,會進行一些自動化的優化,以確保更好的性能和兼容性。這些優化包括
顏色表示法轉換、合併 CSS 屬性、標準化屬性、性能優化
---

## 202505 彈跳視窗 重複開啟視窗 debug
此圖是要顯示關閉彈跳視窗後還有一個相同的,不過截圖截的不好

情境:
透過內層component的點擊事件開啟共用Component的彈跳視窗
但透過外層按鈕點擊事件切到其他頁面再切回來時,卻發現自動開啟重複的彈跳視窗
#### ❗問題症狀:
1. 你關掉彈跳視窗(modalRef?.hide())後,
2. 切換 Menu 頁(Emission ⇄ Consumption)後再切回 Emission,
3. 原本已經關掉的 modal 又「自動開啟」了,甚至「重複開兩個」。

解法:==在 revenue.component.ts 裡加上 takeUntil 與 ngOnDestroy() 清除訂閱。==
```ts=
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
export class CeintensityReportRevenueComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
ngAfterViewInit(): void {
this.ceintensityReportService.getString()
.pipe(takeUntil(this.destroy$))
.subscribe((x) => {
if (x === 'openModal') {
this.openModal(this.templateRef);
}
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
```
✅ 延伸建議
在你開完 modal 並關閉後,也可以「主動重設掉」 service 的值,避免舊值殘留:
```ts=
/** 關閉彈窗 */
cancelModal() {
this.modalRef?.hide();
this.ceintensityReportService.setString(null); // 👉 主動清除 openModal 值
}
```