# โครงการส่วนขยายเบราว์เซอร์ ตอนที่ 2: เรียก API และใช้ Local Storage
## แบบทดสอบก่อนเรียน
[แบบทดสอบก่อนเรียน](https://ff-quizzes.netlify.app/web/quiz/25)
### บทนำ
ในบทเรียนนี้ คุณจะได้เรียนรู้วิธีเรียก API โดยการส่งข้อมูลผ่านฟอร์มของส่วนขยายเบราว์เซอร์ และแสดงผลลัพธ์ในส่วนขยายเบราว์เซอร์ของคุณ นอกจากนี้ คุณจะได้เรียนรู้วิธีจัดเก็บข้อมูลใน Local Storage ของเบราว์เซอร์เพื่อใช้อ้างอิงและใช้งานในอนาคต
✅ ทำตามขั้นตอนที่ระบุไว้ในไฟล์ที่เหมาะสมเพื่อทราบว่าควรวางโค้ดของคุณที่ไหน
### ตั้งค่าองค์ประกอบที่จะใช้งานในส่วนขยาย:
จนถึงตอนนี้ คุณได้สร้าง HTML สำหรับฟอร์มและ `
` ผลลัพธ์สำหรับส่วนขยายเบราว์เซอร์ของคุณแล้ว จากนี้ไป คุณจะต้องทำงานในไฟล์ `/src/index.js` และสร้างส่วนขยายของคุณทีละขั้นตอน อ้างอิงบทเรียน [ก่อนหน้า](../1-about-browsers/README.md) เพื่อดูวิธีตั้งค่าโครงการและกระบวนการสร้าง
เริ่มต้นในไฟล์ `index.js` ของคุณ โดยสร้างตัวแปร `const` เพื่อเก็บค่าที่เกี่ยวข้องกับฟิลด์ต่างๆ:
```JavaScript
// form fields
const form = document.querySelector('.form-data');
const region = document.querySelector('.region-name');
const apiKey = document.querySelector('.api-key');
// results
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 ที่คุณตั้งค่าไว้ใน HTML ในบทเรียนก่อนหน้า
### เพิ่มตัวฟังเหตุการณ์
ต่อไป เพิ่มตัวฟังเหตุการณ์ให้กับฟอร์มและปุ่มล้างข้อมูลที่รีเซ็ตฟอร์ม เพื่อให้เมื่อผู้ใช้ส่งฟอร์มหรือคลิกปุ่มรีเซ็ต จะมีบางสิ่งเกิดขึ้น และเพิ่มคำสั่งเรียกใช้ฟังก์ชันเริ่มต้นแอปที่ด้านล่างของไฟล์:
```JavaScript
form.addEventListener('submit', (e) => handleSubmit(e));
clearBtn.addEventListener('click', (e) => reset(e));
init();
```
✅ สังเกตการใช้รูปแบบย่อเพื่อฟังเหตุการณ์ submit หรือ click และวิธีที่เหตุการณ์ถูกส่งไปยังฟังก์ชัน handleSubmit หรือ reset คุณสามารถเขียนรูปแบบย่อเหล่านี้ในรูปแบบยาวได้หรือไม่? คุณชอบรูปแบบไหนมากกว่า?
### สร้างฟังก์ชัน init() และ reset():
ตอนนี้คุณจะสร้างฟังก์ชันที่เริ่มต้นส่วนขยาย ซึ่งเรียกว่า init():
```JavaScript
function init() {
//if anything is in localStorage, pick it up
const storedApiKey = localStorage.getItem('apiKey');
const storedRegion = localStorage.getItem('regionName');
//set icon to be generic green
//todo
if (storedApiKey === null || storedRegion === null) {
//if we don't have the keys, show the form
form.style.display = 'block';
results.style.display = 'none';
loading.style.display = 'none';
clearBtn.style.display = 'none';
errors.textContent = '';
} else {
//if we have saved keys/regions in localStorage, show results when they load
displayCarbonUsage(storedApiKey, storedRegion);
results.style.display = 'none';
form.style.display = 'none';
clearBtn.style.display = 'block';
}
};
function reset(e) {
e.preventDefault();
//clear local storage for region only
localStorage.removeItem('regionName');
init();
}
```
ในฟังก์ชันนี้ มีตรรกะที่น่าสนใจ อ่านผ่านแล้วคุณเห็นว่าเกิดอะไรขึ้นบ้าง?
- มีการตั้งค่าตัวแปร `const` สองตัวเพื่อตรวจสอบว่าผู้ใช้ได้จัดเก็บ APIKey และรหัสภูมิภาคใน Local Storage หรือไม่
- หากค่าของตัวใดตัวหนึ่งเป็น null ให้แสดงฟอร์มโดยเปลี่ยน style เป็น 'block'
- ซ่อนพื้นที่ผลลัพธ์ การโหลด และปุ่ม clearBtn และตั้งค่าข้อความแสดงข้อผิดพลาดให้เป็นสตริงว่าง
- หากมี key และ region ให้เริ่มกระบวนการ:
- เรียก API เพื่อรับข้อมูลการใช้คาร์บอน
- ซ่อนพื้นที่ผลลัพธ์
- ซ่อนฟอร์ม
- แสดงปุ่มรีเซ็ต
ก่อนดำเนินการต่อ จะเป็นประโยชน์ที่จะเรียนรู้เกี่ยวกับแนวคิดสำคัญที่มีอยู่ในเบราว์เซอร์: [LocalStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) LocalStorage เป็นวิธีที่มีประโยชน์ในการจัดเก็บสตริงในเบราว์เซอร์ในรูปแบบ `key-value` การจัดเก็บข้อมูลประเภทนี้สามารถจัดการได้โดย JavaScript เพื่อจัดการข้อมูลในเบราว์เซอร์ LocalStorage ไม่มีวันหมดอายุ ในขณะที่ SessionStorage ซึ่งเป็นอีกประเภทหนึ่งของการจัดเก็บข้อมูลเว็บ จะถูกล้างเมื่อปิดเบราว์เซอร์ การจัดเก็บข้อมูลแต่ละประเภทมีข้อดีและข้อเสียในการใช้งาน
> หมายเหตุ - ส่วนขยายเบราว์เซอร์ของคุณมี Local Storage ของตัวเอง หน้าต่างเบราว์เซอร์หลักเป็นอินสแตนซ์ที่แตกต่างกันและทำงานแยกกัน
คุณตั้งค่า APIKey ให้มีค่าเป็นสตริง เช่น และคุณสามารถดูว่ามันถูกตั้งค่าใน Edge โดย "ตรวจสอบ" หน้าเว็บ (คุณสามารถคลิกขวาที่เบราว์เซอร์เพื่อทำการตรวจสอบ) และไปที่แท็บ Applications เพื่อดูการจัดเก็บข้อมูล

✅ ลองคิดถึงสถานการณ์ที่คุณไม่ควรจัดเก็บข้อมูลบางอย่างใน LocalStorage โดยทั่วไป การวาง API Keys ใน LocalStorage เป็นความคิดที่ไม่ดี! คุณเห็นเหตุผลหรือไม่? ในกรณีของเรา เนื่องจากแอปของเราเป็นเพียงการเรียนรู้และจะไม่ถูกเผยแพร่ในร้านแอป เราจะใช้วิธีนี้
สังเกตว่าคุณใช้ Web API เพื่อจัดการ LocalStorage โดยใช้ `getItem()`, `setItem()` หรือ `removeItem()` ซึ่งได้รับการสนับสนุนอย่างกว้างขวางในเบราว์เซอร์
ก่อนสร้างฟังก์ชัน `displayCarbonUsage()` ที่ถูกเรียกใน `init()` ให้สร้างฟังก์ชันเพื่อจัดการการส่งฟอร์มเริ่มต้นก่อน
### จัดการการส่งฟอร์ม
สร้างฟังก์ชันชื่อ `handleSubmit` ที่รับอาร์กิวเมนต์เหตุการณ์ `(e)` หยุดเหตุการณ์ไม่ให้แพร่กระจาย (ในกรณีนี้ เราต้องการหยุดเบราว์เซอร์ไม่ให้รีเฟรช) และเรียกฟังก์ชันใหม่ `setUpUser` โดยส่งอาร์กิวเมนต์ `apiKey.value` และ `region.value` ในวิธีนี้ คุณใช้ค่าทั้งสองที่นำเข้าผ่านฟอร์มเริ่มต้นเมื่อฟิลด์ที่เหมาะสมถูกเติมเต็ม
```JavaScript
function handleSubmit(e) {
e.preventDefault();
setUpUser(apiKey.value, region.value);
}
```
✅ ทบทวนความจำ - HTML ที่คุณตั้งค่าไว้ในบทเรียนที่แล้วมีฟิลด์อินพุตสองฟิลด์ที่ `values` ถูกจับผ่าน `const` ที่คุณตั้งค่าไว้ที่ด้านบนของไฟล์ และทั้งสองฟิลด์เป็น `required` ดังนั้นเบราว์เซอร์จะหยุดผู้ใช้ไม่ให้ป้อนค่าที่เป็น null
### ตั้งค่าผู้ใช้
ต่อไปยังฟังก์ชัน `setUpUser` ที่นี่คุณตั้งค่าค่า Local Storage สำหรับ apiKey และ regionName เพิ่มฟังก์ชันใหม่:
```JavaScript
function setUpUser(apiKey, regionName) {
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('regionName', regionName);
loading.style.display = 'block';
errors.textContent = '';
clearBtn.style.display = 'block';
//make initial call
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 ที่มีให้สำหรับนักพัฒนา รูปแบบใดที่คุณชอบ?
มีสิ่งสำคัญที่ควรทราบเกี่ยวกับฟังก์ชันนี้ ก่อนอื่น สังเกตคำสำคัญ [`async`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function) การเขียนฟังก์ชันของคุณให้ทำงานแบบอะซิงโครนัสหมายความว่ามันจะรอการดำเนินการ เช่น การคืนค่าข้อมูล ก่อนดำเนินการต่อ
นี่คือวิดีโอสั้นๆ เกี่ยวกับ `async`:
[](https://youtube.com/watch?v=YwmlRkrxvkk "Async และ Await สำหรับการจัดการ promises")
> 🎥 คลิกที่ภาพด้านบนเพื่อดูวิดีโอเกี่ยวกับ async/await
สร้างฟังก์ชันใหม่เพื่อสอบถาม API C02Signal:
```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 คืนค่าข้อมูล เนื่องจากคุณไม่สามารถควบคุมความเร็วที่ API จะตอบกลับ (หรืออาจไม่ตอบกลับเลย!) คุณจำเป็นต้องจัดการความไม่แน่นอนนี้โดยเรียกมันแบบอะซิงโครนัส
- คุณกำลังสอบถาม API co2signal เพื่อรับข้อมูลของภูมิภาคของคุณ โดยใช้ API Key ของคุณ ในการใช้ key นั้น คุณต้องใช้การตรวจสอบสิทธิ์ในพารามิเตอร์ header
- เมื่อ 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://ff-quizzes.netlify.app/web/quiz/26)
## ทบทวนและศึกษาด้วยตนเอง
คุณได้เรียนรู้เกี่ยวกับ LocalStorage และ API ในบทเรียนนี้ ซึ่งทั้งสองอย่างมีประโยชน์มากสำหรับนักพัฒนาเว็บมืออาชีพ คุณสามารถคิดได้ไหมว่า LocalStorage และ API ทำงานร่วมกันอย่างไร? ลองคิดดูว่าคุณจะออกแบบเว็บไซต์ที่จัดเก็บรายการเพื่อใช้งานโดย API อย่างไร
## งานที่ได้รับมอบหมาย
[Adopt an API](assignment.md)
---
**ข้อจำกัดความรับผิดชอบ**:
เอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่แม่นยำ เอกสารต้นฉบับในภาษาต้นทางควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามนุษย์ที่เป็นมืออาชีพ เราจะไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความที่ผิดพลาดซึ่งเกิดจากการใช้การแปลนี้