# Проект за браузърно разширение, част 2: Извикване на API, използване на локално съхранение ## Предварителен тест [Предварителен тест](https://ff-quizzes.netlify.app/web/quiz/25) ## Въведение Спомняте ли си браузърното разширение, което започнахте да изграждате? В момента имате добре изглеждаща форма, но тя е по същество статична. Днес ще я оживим, като я свържем с реални данни и й дадем памет. Помислете за компютрите за управление на мисията "Аполо" - те не просто показваха фиксирана информация. Те постоянно комуникираха с космическия кораб, актуализираха данни за телеметрията и запомняха критични параметри на мисията. Това е видът динамично поведение, което ще изградим днес. Вашето разширение ще се свърже с интернет, ще извлече реални данни за околната среда и ще запомни вашите настройки за следващия път. Интеграцията с API може да звучи сложно, но всъщност е просто обучение на вашия код как да комуникира с други услуги. Независимо дали извличате данни за времето, социални медии или информация за въглеродния отпечатък, както ще направим днес, всичко се свежда до установяване на тези цифрови връзки. Ще разгледаме и как браузърите могат да запазват информация - подобно на това как библиотеките използват каталози с карти, за да запомнят къде се намират книгите. До края на този урок ще имате браузърно разширение, което извлича реални данни, съхранява потребителски предпочитания и предоставя гладко изживяване. Да започваме! ✅ Следвайте номерираните сегменти в съответните файлове, за да знаете къде да поставите вашия код. ## Настройка на елементите за манипулиране в разширението Преди вашият JavaScript да може да манипулира интерфейса, той трябва да има препратки към конкретни HTML елементи. Помислете за това като за телескоп, който трябва да бъде насочен към определени звезди - преди Галилей да може да изучава луните на Юпитер, той трябваше да локализира и фокусира върху самия Юпитер. Във вашия файл `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'); ``` **Ето какво прави този код:** - **Улавя** елементи от формата, използвайки `document.querySelector()` с CSS клас селектори - **Създава** препратки към полетата за въвеждане на име на регион и API ключ - **Установява** връзки към елементи за показване на резултати за данни за въглеродна употреба - **Настройва** достъп до елементи на потребителския интерфейс като индикатори за зареждане и съобщения за грешки - **Съхранява** всяка препратка към елемент в променлива `const` за лесно повторно използване в целия код ## Добавяне на слушатели за събития Сега ще направим вашето разширение да реагира на действията на потребителя. Слушателите за събития са начинът, по който вашият код следи взаимодействията на потребителя. Помислете за тях като за операторите в ранните телефонни централи - те слушаха входящите обаждания и свързваха правилните линии, когато някой искаше да направи връзка. ```javascript form.addEventListener('submit', (e) => handleSubmit(e)); clearBtn.addEventListener('click', (e) => reset(e)); init(); ``` **Разбиране на тези концепции:** - **Прикрепя** слушател за изпращане към формата, който се задейства, когато потребителите натиснат Enter или кликнат върху изпращане - **Свързва** слушател за клик към бутона за изчистване за нулиране на формата - **Предава** обекта на събитието `(e)` към функции за обработка за допълнителен контрол - **Извиква** функцията `init()` незабавно, за да настрои началното състояние на вашето разширение ✅ Обърнете внимание на съкратения синтаксис на стрелковите функции, използван тук. Този модерен подход в JavaScript е по-чист от традиционните изрази на функции, но и двата работят еднакво добре! ## Създаване на функции за инициализация и нулиране Нека създадем логиката за инициализация на вашето разширение. Функцията `init()` е като навигационната система на кораб, която проверява своите инструменти - тя определя текущото състояние и съответно настройва интерфейса. Тя проверява дали някой е използвал вашето разширение преди и зарежда предишните му настройки. Функцията `reset()` предоставя на потребителите ново начало - подобно на това как учените нулират своите инструменти между експерименти, за да осигурят чисти данни. ```javascript function init() { // Check if user has previously saved API credentials const storedApiKey = localStorage.getItem('apiKey'); const storedRegion = localStorage.getItem('regionName'); // Set extension icon to generic green (placeholder for future lesson) // TODO: Implement icon update in next lesson if (storedApiKey === null || storedRegion === null) { // First-time user: show the setup form form.style.display = 'block'; results.style.display = 'none'; loading.style.display = 'none'; clearBtn.style.display = 'none'; errors.textContent = ''; } else { // Returning user: load their saved data automatically displayCarbonUsage(storedApiKey, storedRegion); results.style.display = 'none'; form.style.display = 'none'; clearBtn.style.display = 'block'; } } function reset(e) { e.preventDefault(); // Clear stored region to allow user to choose a new location localStorage.removeItem('regionName'); // Restart the initialization process init(); } ``` **Разбивка на случващото се тук:** - **Извлича** съхранения API ключ и регион от локалното съхранение на браузъра - **Проверява** дали това е първият път, когато потребителят използва разширението (няма съхранени данни) или е завръщащ се потребител - **Показва** формата за настройка за нови потребители и скрива други елементи на интерфейса - **Зарежда** автоматично запазените данни за завръщащи се потребители и показва опцията за нулиране - **Управлява** състоянието на потребителския интерфейс въз основа на наличните данни **Основни концепции за локалното съхранение:** - **Запазва** данни между сесиите на браузъра (за разлика от session storage) - **Съхранява** данни като двойки ключ-стойност, използвайки `getItem()` и `setItem()` - **Връща** `null`, когато няма данни за даден ключ - **Осигурява** прост начин за запомняне на потребителски предпочитания и настройки > 💡 **Разбиране на съхранението в браузъра**: [LocalStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) е като да дадете на вашето разширение постоянна памет. Помислете как древната библиотека в Александрия е съхранявала свитъци - информацията остава достъпна, дори когато учените напускат и се връщат. > > **Основни характеристики:** > - **Запазва** данни дори след затваряне на браузъра > - **Оцелява** след рестартиране на компютъра и сривове на браузъра > - **Осигурява** значително пространство за съхранение на потребителски предпочитания > - **Предоставя** незабавен достъп без забавяне от мрежата > **Важно уточнение**: Вашето браузърно разширение има собствено изолирано локално съхранение, което е отделено от обикновените уеб страници. Това осигурява сигурност и предотвратява конфликти с други уебсайтове. Можете да видите съхранените данни, като отворите инструментите за разработчици на браузъра (F12), отидете на раздела **Application** и разширите секцията **Local Storage**. ![Панел за локално съхранение](../../../../translated_images/localstorage.472f8147b6a3f8d141d9551c95a2da610ac9a3c6a73d4a1c224081c98bae09d9.bg.png) > ⚠️ **Съображения за сигурност**: В производствени приложения съхранението на API ключове в LocalStorage представлява риск за сигурността, тъй като JavaScript може да достъпи тези данни. За учебни цели този подход е подходящ, но реалните приложения трябва да използват сигурно съхранение на сървър за чувствителни данни. ## Обработка на изпращане на форма Сега ще обработим какво се случва, когато някой изпрати вашата форма. По подразбиране браузърите презареждат страницата при изпращане на форми, но ние ще прихванем това поведение, за да създадем по-гладко изживяване. Този подход е подобен на начина, по който центърът за управление на мисии обработва комуникациите със космически кораби - вместо да рестартира цялата система за всяко предаване, те поддържат непрекъсната работа, докато обработват нова информация. Създайте функция, която улавя събитието за изпращане на форма и извлича въведените от потребителя данни: ```javascript function handleSubmit(e) { e.preventDefault(); setUpUser(apiKey.value, region.value); } ``` **В горното сме:** - **Предотвратява** стандартното поведение при изпращане на форма, което би презаредило страницата - **Извлича** стойности, въведени от потребителя, от полетата за API ключ и регион - **Предава** данните от формата към функцията `setUpUser()` за обработка - **Поддържа** поведение на приложение с една страница, като избягва презареждането на страницата ✅ Запомнете, че вашите HTML полета за форма включват атрибута `required`, така че браузърът автоматично валидира, че потребителите предоставят както API ключ, така и регион, преди тази функция да се изпълни. ## Настройка на потребителски предпочитания Функцията `setUpUser` е отговорна за запазването на потребителските данни и инициирането на първото API извикване. Това създава плавен преход от настройката към показването на резултати. ```javascript function setUpUser(apiKey, regionName) { // Save user credentials for future sessions localStorage.setItem('apiKey', apiKey); localStorage.setItem('regionName', regionName); // Update UI to show loading state loading.style.display = 'block'; errors.textContent = ''; clearBtn.style.display = 'block'; // Fetch carbon usage data with user's credentials displayCarbonUsage(apiKey, regionName); } ``` **Стъпка по стъпка, ето какво се случва:** - **Запазва** API ключа и името на региона в локалното съхранение за бъдеща употреба - **Показва** индикатор за зареждане, за да информира потребителите, че данните се извличат - **Изчиства** всички предишни съобщения за грешки от дисплея - **Разкрива** бутона за изчистване, за да могат потребителите да нулират своите настройки по-късно - **Инициира** API извикването за извличане на реални данни за въглеродна употреба Тази функция създава безпроблемно потребителско изживяване, като управлява както запазването на данни, така и актуализациите на потребителския интерфейс в едно координирано действие. ## Показване на данни за въглеродна употреба Сега ще свържем вашето разширение с външни източници на данни чрез API. Това трансформира вашето разширение от самостоятелен инструмент в нещо, което може да получава информация в реално време от интернет. **Разбиране на API** [API](https://www.webopedia.com/TERM/A/API.html) са начинът, по който различни приложения комуникират помежду си. Помислете за тях като за телеграфната система, която свързва отдалечени градове през 19-ти век - операторите изпращаха заявки до отдалечени станции и получаваха отговори с исканата информация. Всеки път, когато проверявате социални медии, задавате въпрос на гласов асистент или използвате приложение за доставка, API улесняват тези обмен на данни. **Основни концепции за REST API:** - **REST** означава "Представително състояние на трансфер" - **Използва** стандартни HTTP методи (GET, POST, PUT, DELETE) за взаимодействие с данни - **Връща** данни в предвидими формати, обикновено JSON - **Осигурява** последователни, базирани на URL крайни точки за различни типове заявки ✅ [CO2 Signal API](https://www.co2signal.com/), който ще използваме, предоставя данни за въглеродната интензивност на електрическите мрежи по целия свят в реално време. Това помага на потребителите да разберат екологичния отпечатък на тяхното електрическо потребление! > 💡 **Разбиране на асинхронния JavaScript**: [`async` ключовата дума](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function) позволява на вашия код да обработва множество операции едновременно. Когато поискате данни от сървър, не искате цялото разширение да замръзне - това би било като въздушен контрол, който спира всички операции, докато чака един самолет да отговори. > > **Основни предимства:** > - **Поддържа** отзивчивостта на разширението, докато данните се зареждат > - **Позволява** на друг код да продължи да се изпълнява по време на мрежови заявки > - **Подобрява** четимостта на кода в сравнение с традиционните модели на обратни обаждания > - **Позволява** елегантно обработване на грешки при мрежови проблеми Ето кратко видео за `async`: [![Async и Await за управление на обещания](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async и Await за управление на обещания") > 🎥 Кликнете върху изображението по-горе за видео за async/await. Създайте функцията за извличане и показване на данни за въглеродна употреба: ```javascript // Modern fetch API approach (no external dependencies needed) async function displayCarbonUsage(apiKey, region) { try { // Fetch carbon intensity data from CO2 Signal API const response = await fetch('https://api.co2signal.com/v1/latest', { method: 'GET', headers: { 'auth-token': apiKey, 'Content-Type': 'application/json' }, // Add query parameters for the specific region ...new URLSearchParams({ countryCode: region }) && { url: `https://api.co2signal.com/v1/latest?countryCode=${region}` } }); // Check if the API request was successful if (!response.ok) { throw new Error(`API request failed: ${response.status}`); } const data = await response.json(); const carbonData = data.data; // Calculate rounded carbon intensity value const carbonIntensity = Math.round(carbonData.carbonIntensity); // Update the user interface with fetched data loading.style.display = 'none'; form.style.display = 'none'; myregion.textContent = region.toUpperCase(); usage.textContent = `${carbonIntensity} grams (grams CO₂ emitted per kilowatt hour)`; fossilfuel.textContent = `${carbonData.fossilFuelPercentage.toFixed(2)}% (percentage of fossil fuels used to generate electricity)`; results.style.display = 'block'; // TODO: calculateColor(carbonIntensity) - implement in next lesson } catch (error) { console.error('Error fetching carbon data:', error); // Show user-friendly error message loading.style.display = 'none'; results.style.display = 'none'; errors.textContent = 'Sorry, we couldn\'t fetch data for that region. Please check your API key and region code.'; } } ``` **Разбивка на случващото се тук:** - **Използва** модерния `fetch()` API вместо външни библиотеки като Axios за по-чист код без зависимости - **Прилага** правилна проверка за грешки с `response.ok`, за да улови ранни неуспехи на API - **Обработва** асинхронни операции с `async/await` за по-четим поток на кода - **Автентифицира** с CO2 Signal API, използвайки заглавката `auth-token` - **Парсира** JSON отговори и извлича информация за въглеродната интензивност - **Актуализира** множество елементи на потребителския интерфейс с форматирани данни за околната среда - **Осигурява** удобни за потребителя съобщения за грешки, когато API заявките се провалят **Основни модерни концепции в JavaScript:** - **Шаблонни литерали** със синтаксис `${}` за чисто форматиране на текст - **Обработка на грешки** с try/catch блокове за надеждни приложения - **Async/await** модел за елегантно управление на мрежови заявки - **Деструктуриране на обекти** за извличане на специфични данни от API отговори - **Верижно свързване на методи** за множество манипулации на DOM ✅ Тази функция демонстрира няколко важни концепции за уеб разработка - комуникация с външни сървъри, обработка на автентификация, обработка на данни, актуализиране на интерфейси и елегантно управление на грешки. Това са основни умения, които професионалните разработчици използват редовно. 🎉 **Какво постигнахте:** Създадохте браузърно разширение, което: - **Свързва се** с интернет и извлича реални данни за околната среда - **Запазва** потребителски настройки между сесиите - **Обработва** грешки елегантно, вместо да се срива - **Осигурява** гладко, професионално потребителско изживяване Тествайте вашата работа, като изпълните `npm run build` и обновите вашето разширение в браузъра. Сега имате функционален тракер за въглеродния отпечатък. В следващия урок ще добавим функционалност за динамични икони, за да завършим разширението. --- ## Предизвикателство с GitHub Copilot Agent 🚀 Използвайте режима Agent, за да изпълните следното предизвикателство: **Описание:** Подобрете браузърното В този урок научихте за LocalStorage и API-та, и двете са много полезни за професионалния уеб разработчик. Можете ли да помислите как тези две неща работят заедно? Помислете как бихте проектирали уеб сайт, който съхранява елементи, които да бъдат използвани от API. ## Задача [Осиновете API](assignment.md) --- **Отказ от отговорност**: Този документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за каквито и да е недоразумения или погрешни интерпретации, произтичащи от използването на този превод.