---
# System prepended metadata

title: 前端開發 實務歷程
tags: [Work]

---


###### 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
:::


![](https://hackmd.io/_uploads/SkOM_v_23.png)

### ==送正式的approve都不能自己按==
![image](https://hackmd.io/_uploads/SyEokFRTa.png)


## 簡易執行
版本：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

酒窖
![image](https://hackmd.io/_uploads/SJqd356M1g.png)


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的時候可能會無法引用


### 看文件注意事項
> ### 模組不是資料夾的意思
只是個分類(總之不用管)
看`資料夾名稱`就好，那就是第一層要建的資料夾

啊建的時後確認下建立的位置嘿

![image](https://hackmd.io/_uploads/B1OcbYZCp.png)



## 創建架構執行步驟 (幕僚--緯澤註)

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畫面
    ![image](https://hackmd.io/_uploads/HytzHUeXR.png)


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傳遞資料或參數，可能資料未傳遞完就完成跳頁，會造成訂閱判定無資料
情境：酒窖列表，藉由點擊庫存數量，切換到酒窖(分倉)報表
問題：但會出現報表內容重複的問題，看似資料有傳到，從頁面畫面來看會出現重複新增報表內容的狀況

![image](https://hackmd.io/_uploads/S1wJuxt1A.png)

從程式面來看，這是訂閱判定沒資料的結果
=> 酒窖列表的庫存連結點擊事件
![image](https://hackmd.io/_uploads/SJqrtgF10.png)

=> 報表頁初始設定
![image](https://hackmd.io/_uploads/BJMecxFJ0.png)


原因：因為第一次資料還沒傳完，router就打斷，導致第二次點即會出現兩次傳遞的狀況
![image](https://hackmd.io/_uploads/HyCMdltk0.png)



解法
![image](https://hackmd.io/_uploads/SkBo5lFkC.png)


### loseFocus 離開輸入框執行事件
以 input 來說 (blur) = "事件名稱()"



## ERP 程式目錄設定
![image](https://hackmd.io/_uploads/BJlOGVxmA.png)

通常選OA_TEST
![image](https://hackmd.io/_uploads/HyHifNeXC.png)

進入目錄程式後，新增目錄程式
程式編號、程式描述填程式名稱，程式類型一般填Smart 程式(就是指新程式)
![image](https://hackmd.io/_uploads/ryUxXVxQ0.png)

再次進入頁面後，查詢確認新增
![image](https://hackmd.io/_uploads/rkss7VlmR.png)

進入目錄設定
![image](https://hackmd.io/_uploads/rk5IENxQA.png)


![image](https://hackmd.io/_uploads/Sk_irNeXA.png)

放到對應位置
![image](https://hackmd.io/_uploads/H1T4LNgQR.png)

放哪裡通常問經理
![image](https://hackmd.io/_uploads/SydDUVgQR.png)

## 什麼是變更檢測（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 屬性、標準化屬性、性能優化

---

![image](https://hackmd.io/_uploads/Sk6U0Kx5ye.png)


## 202505 彈跳視窗 重複開啟視窗 debug

此圖是要顯示關閉彈跳視窗後還有一個相同的，不過截圖截的不好
![image](https://hackmd.io/_uploads/BJYg5MXflg.png)

情境：
透過內層component的點擊事件開啟共用Component的彈跳視窗
但透過外層按鈕點擊事件切到其他頁面再切回來時，卻發現自動開啟重複的彈跳視窗

#### ❗問題症狀：
1. 你關掉彈跳視窗（modalRef?.hide()）後，
2. 切換 Menu 頁（Emission ⇄ Consumption）後再切回 Emission，
3. 原本已經關掉的 modal 又「自動開啟」了，甚至「重複開兩個」。

![image](https://hackmd.io/_uploads/HJX8jfXGee.png)

解法：==在 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 值
}
```


