# 瀏覽器擴充功能專案 Part 1：呼叫 API，使用 Local Storage

## 課前測驗

[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/25?loc=zh_tw)

### 大綱

在這堂課中，藉由傳遞你的擴充功能表單並顯示結果來呼叫 API。此外，你會了解如何儲存資料到瀏覽器的 Local Storage 中給未來使用。

✅ 請參考下列程式碼段，加入程式碼到檔案適當的位置

### 設定控制擴充功能的元素：

現在你有已建好的 HTML 表單與結果區 `<div>`。接下來，你需要在 `/src/index.js` 做一些處理，一點一點地構築出你的擴充功能。參考[前一堂課程](../../1-about-browsers/translations/README.zh-tw.md)來設置你的專案與了解建制過程。

處理 `index.js` 檔案，建立一些 `const` 變數來儲存不同用途的數值：

```JavaScript
// 表單區域
const form = document.querySelector('.form-data');
const region = document.querySelector('.region-name');
const apiKey = document.querySelector('.api-key');

// 結果區域
const errors = document.querySelector('.errors');
const loading = document.querySelector('.loading');
const results = document.querySelector('.result-container');
const usage = document.querySelector('.carbon-usage');
const fossilfuel = document.querySelector('.fossil-fuel');
const myregion = document.querySelector('.my-region');
const clearBtn = document.querySelector('.clear-btn');
```

這些區域會被 CSS class 給參考，它們在前一堂課中已經被你設定好了。

### 新增監聽者

接下來，新增提交與重置表單的事件監聽者與按鈕，讓使用者能提交表單或是點擊重置鈕時，事件會發生。新增初始化呼叫處理到應用中，在檔案的最下方新增：

```JavaScript
form.addEventListener('submit', (e) => handleSubmit(e));
clearBtn.addEventListener('click', (e) => reset(e));
init();
```

✅ 注意提交事件與點擊事件的寫法，事件是如何被傳入到 handleSubmit 或是 reset 函式中的。你能在不改變功能的情況下，改寫成較長的格式嗎？你比較喜歡哪一種寫法？

### 建立 init() 函式與 reset() 函式：

現在你需要建立函式 init()，處理應用程式的初始化部分：

```JavaScript
function init() {
	//如果任何東西存在 localStorage 中，取出來
	const storedApiKey = localStorage.getItem('apiKey');
	const storedRegion = localStorage.getItem('regionName');

	//設定 icon 為通用綠色
	//todo

	if (storedApiKey === null || storedRegion === null) {
		//如果沒有 keys，顯示表單
		form.style.display = 'block';
		results.style.display = 'none';
		loading.style.display = 'none';
		clearBtn.style.display = 'none';
		errors.textContent = '';
	} else {
        //localStorage 有 saved keys/regions，顯示結果
        displayCarbonUsage(storedApiKey, storedRegion);
		results.style.display = 'none';
		form.style.display = 'none';
		clearBtn.style.display = 'block';
	}
};

function reset(e) {
	e.preventDefault();
	//只清除 local storage 國家區域代碼
	localStorage.removeItem('regionName');
	init();
}

```
在函式中，有一些有趣的邏輯。閱讀它們，你看出發生什麼事嗎？

- 兩個 `const` 被設定為檢查用戶是否有儲存 APIKey 與國家區域代碼在 local storage 中。
- 若兩者皆為 null，將造型設為 'block' 來顯示表單
- 隱藏 results、loading 與 clearBtn，設定 error 文字為空字串
- 若存在 key 與代碼，開始新的流程：
  - 呼叫 API 取得碳排放資訊
  - 隱藏結果區域
  - 隱藏表單
  - 顯示重置按鈕

在下一步之前，你可以學習一些瀏覽器的重要成員：[LocalStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage)。 LocalStorage 是瀏覽器儲存字串的有效方法，以 `key-value` 配對兩兩一組。這種儲存型態可以被 JavaScript 管理並控制瀏覽器的資料。LocalStorage 沒有期限，而另一款網頁儲存 SessionStorage 會在瀏覽器關閉時清除內容。不同的儲存方式有各自的優缺點。

> 注意 ── 你的瀏覽器擴充套件有自己的 local storage。主瀏覽器視窗是不同的個體，兩者會做各自的行為。

你設定 APIKey 紀錄字串數值。你可以在 Edge 瀏覽器上「檢查」一個網頁 (右鍵瀏覽器來檢查)，在 Applications 標籤中觀察儲存區的使用情況。

![Local storage 區域](../images/localstorage.png)

✅ 想想那些情況你不需要儲存資料到 LocalStorage 中。總體而言，將 API Keys 放在 LocalStorage 是個很糟糕的想法！你知道為什麼嗎？在我們的例子中，我們的應用程式是以教學為目的，並不會發布在應用程式商店中，所以我們選擇此中處理方式。

你可以發現網頁 API 能處理 LocalStorage，使用 `getItem()`、`setItem()` 或是 `removeItem()`。它們廣泛地支援不同的瀏覽器。

在建立函式 `init()` 中的函式 `displayCarbonUsage()` 之前，我們先建立表單提交初始化的功能。

### 處理表單提交

建立函式 `handleSubmit`，接收事件參數 `(e)`。終止網頁移轉的事件(在本例子中，我們終止瀏覽器刷新的處理)並呼叫新的函式 `setUpUser`，傳送參數 `apiKey.value` 與 `region.value`。藉由這個方式，你能將兩個初始表單的數值正確地移轉到適合的位置。

```JavaScript
function handleSubmit(e) {
	e.preventDefault();
	setUpUser(apiKey.value, region.value);
}
```
✅ 刷新你的記憶 ── 上堂課中的 HTML 檔案開頭有兩個輸入區域，它們的 `values` 被存到 `const` 中，並且被定為 `required`，表示瀏覽器禁止使用者輸入空值。

### 設定使用者

來到函式 `setUpUser`，這裡你能找到 apiKey 與 regionName 被存到 Local Storage 中。新增函式：

```JavaScript
function setUpUser(apiKey, regionName) {
	localStorage.setItem('apiKey', apiKey);
	localStorage.setItem('regionName', regionName);
	loading.style.display = 'block';
	errors.textContent = '';
	clearBtn.style.display = 'block';
	//建立初始化呼叫
	displayCarbonUsage(apiKey, regionName);
}
```
這個函式設定當 API 被呼叫時，顯示讀取訊息。到這裡，你即將建立這個擴充功能專案最重要的函式！

### 顯示碳排放量

最後，是時候查詢 API 了！

在前往下一步前，我們先來討論何謂 API。API，[Application Programming Interfaces](https://www.webopedia.com/TERM/A/API.html)，是網頁開發者工具箱內最重要的成員。它們提供程式標準的互動模式與溝通介面，舉例來說，如果你建立一個需要存取資料庫的網頁，資料庫方可能就有人建立了 API 供你使用。API 有各式各樣的種類，最普遍使用的為[REST API](https://www.smashingmagazine.com/2018/01/understanding-using-rest-api/)。

✅ 'REST' 全名為 'Representational State Transfer'，提供各式各樣 URL 形式來抓取資料。對網路開發者的 API 種類做一點研究，什麼形式的 API 最吸引你？

這條函式中有一個重要到值得紀錄的事情。第一點為[關鍵字 `async`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function)。讓你的函式非同步地執行，在行為完成前做等待，譬如資料被回傳。

這裡有一個簡短的影片介紹 `async`：

[![Async 與 Await 處理 promises 物件](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async 與 Await 處理 promises 物件")

> 點擊上方圖片以觀賞關於 async/await 的影片。

建立新的函式來詢問 C02Signal 的 API：

```JavaScript
import axios from '../node_modules/axios';

async function displayCarbonUsage(apiKey, region) {
	try {
		await axios
			.get('https://api.co2signal.com/v1/latest', {
				params: {
					countryCode: region,
				},
				headers: {
					'auth-token': apiKey,
				},
			})
			.then((response) => {
				let CO2 = Math.floor(response.data.data.carbonIntensity);

				//calculateColor(CO2);

				loading.style.display = 'none';
				form.style.display = 'none';
				myregion.textContent = region;
				usage.textContent =
					Math.round(response.data.data.carbonIntensity) + ' grams (grams C02 emitted per kilowatt hour)';
				fossilfuel.textContent =
					response.data.data.fossilFuelPercentage.toFixed(2) +
					'% (percentage of fossil fuels used to generate electricity)';
				results.style.display = 'block';
			});
	} catch (error) {
		console.log(error);
		loading.style.display = 'none';
		results.style.display = 'none';
		errors.textContent = 'Sorry, we have no data for the region you have requested.';
	}
}
```

這是一個挺大的函式，發生了什麼事？

- 遵循程式實踐過程，你使用關鍵字 `async` 讓函式非同步地作行為。函式內的 `try/catch` 區塊會在 API 回傳資料時回傳 promise 物件。因為我們無法控制 API 會多快地回應訊息(甚至無法回應訊息！)，你需要處理這種不確定性的時序關係。 
- 藉由提供 API Key 訪問 co2signal API 以取得你的地區資料。要使用這把鑰匙，你必須在網頁標頭中新增認證參數。
- 當 API 回應時，你將各種物件填入回傳的數值，並輸出到畫面上中。
- 如果發生錯誤，或沒有結果產生，輸出錯誤訊息。

✅ 非同步程式設計是一種實用的工具。閱讀[更多使用方法](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function)設定非同步程式的程式碼。

恭喜你！當你建制你的專案(`npm run build`)並在瀏覽器上刷新功能，你有個可以運作的應用套件了！現在只差圖示無法正常顯示，我們會在下一堂課中修正它。

---

## 🚀 挑戰

我們在課程中討論了不同種類的 API。選擇一樣網頁 API 並做更深度的研究。舉例來說，看看瀏覽器內支援的 API 如 [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API)。依你看，什麼決定了 API 的優劣？

## 課後測驗

[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/26?loc=zh_tw)

## 複習與自學

這堂課你學會關於 LocalStorage 與 API，它們對資深網頁開發者提供很大的幫助。你能想想這兩樣東西如何彼此相互合作呢？想想你會如何建構你的網頁，讓 API 得以使用你所儲存的資料。

## 作業

[認領一項 API](assignment.zh-tw.md)

