Всім привіт! Цього місяця ми починаємо серію статей, присвячених LWC. Разом ми розберемось у структурі та принципах розробки LWC компонентів, базових принципах розмітки, комунікації між компонентами та звязку компонентів з Apex.
Отже давайте почнемо наше знайомство з Lightning Web Components або LWC. LWC — це заснована на сучасних стандартах веб програмування модель для створення веб-компонентів, яка працює в Salesforce. Зараз ми розглянемо основи LWC, зокрема, що це таке, його структуру та теорію, рекомендації Salesforce щодо створення компонентів, найкращі практики використання LWC тощо. Давайте розпочнемо!
Що таке LWC?
Lightning Web Components або LWC — це легкий у використанні фреймворк. Модель програмування якого використовує сучасні веб-стандарти, такі як веб-компоненти, користувацькі елементи, тіньовий DOM і модулі ECMAScript, для створення багаторазово використовуваних компонентів інтерфейсу користувача. LWC підтримується Salesforce, що означає, що ви можете створювати, тестувати та розгортати компоненти LWC безпосередньо на платформі Salesforce.
LWC використовує компонентну архітектуру, яка складається з файлів HTML, CSS і JavaScript. LWC також використовує ієрархічну структуру, яка дозволяє компонентам складатися з інших компонентів. Це дозволяє розробникам створювати складні інтерфейси користувача шляхом поєднання простих компонентів, які можна багаторазово використовувати.
У LWC кожен компонент визначається як клас JavaScript, який розширює базовий клас LightningElement. Цей клас містить властивості, методи та хуки життєвого циклу, які визначають поведінку компонента.
LWC надає набір базових інструментів, які допомагають вам керувати станом і життєвим циклом ваших компонентів. Деякі з ключових інструментів включають:
- ConnectedCallback() — це хук життєвого циклу, який викликається, коли компонент вставляється в DOM. Це означає, що компонент завершив рендеринг і готовий до відображення. Ви можете використовувати цей хук для виконання будь-яких завдань, які вимагають доступу до DOM, наприклад, налаштування прослуховувачів подій або отримання даних із сервера.
connectedCallback() {
// Add a window resize event listener
window.addEventListener('resize', this.handleWindowResize);
}
handleWindowResize(event) {
// Your logic for handling the window resize event goes here
// You can access event properties like event.target, event.clientX, event.clientY, etc.
// For example, you can update component attributes or call methods.
console.log('Window resized:', event.target.innerWidth, event.target.innerHeight);
}
- DisconnectedCallback() — це ще один хук життєвого циклу, який викликається, коли компонент видаляється з DOM. Це означає, що компонент більше не видно, та він знищується. Ви можете використовувати цей хук для виконання будь-яких завдань очищення, таких як видалення слухачів подій або скасування будь-яких запитів, що очікують на розгляд.
disconnectedCallback() {
// Remove the window resize event listener when the component is disconnected
window.removeEventListener('resize', this.handleWindowResize);
}
- RenderedCallback() — це хук життєвого циклу, який викликається після кожного циклу візуалізації компонента. Це означає, що він викликається кожного разу, коли компонент відображається, незалежно від того, чи відбувається це вперше, чи наступні рази. Ви можете використовувати цей хук для виконання будь-яких завдань, які вимагають доступу до візуалізованої DOM, наприклад, маніпулювання DOM або оновлення стану компонента. У прикладі ви можете побачити, що renderedCallback() додає клас стилю до елемента кнопки кожного разу, коли компонент відображається. Це дає вам змогу змінювати DOM після того, як він був відтворений, що може бути корисним у певних сценаріях. Важливо відзначити, що renderedCallback() може бути викликаним кілька разів протягом життєвого циклу компонента, тому слід бути обережним, щоб не виконувати довгі та ресурсно витратні операції або робити занадто багато викликів API у цьому хуку.
@api buttonStyle;
renderedCallback() {
// When we finished Render we adding some spec ific style
// class to our button which was passed from parrent LWC
const button = this.template.querySelector('button');
button.classList.add(this.buttonStyle);
}
- errorCallback() - це хук життєвого циклу, який викликається в разі виникнення помилки під час візуалізації компонента у LWC. Він надає можливість обробити й відловити помилку та вжити необхідних заходів для відновлення стану компонента або відображення відповідного повідомлення про помилку. Цей хук викликається після виникнення помилки та дозволяє виконувати додаткові дії, такі як запис помилки в журнал, сповіщення користувача або інші маніпуляції з DOM. Використовуючи errorCallback(), ви можете керувати відображенням та реакцією компонента на помилки, що допомагає забезпечити більш стабільну та надійну роботу вашого LWC компонента.
errorCallback(error, stack) {
// We catch errors in error callback and can work with them as we need
console.error('Error message:', error);
console.error('Error stack trace:', stack);
this.doSomething();
}
doSomething() {
// Perform some actions
}
Тепер давайте поговоримо про основу використання змінних у LWC.
У LWC є два основних рівні змінних: змінні рівня класу та змінні рівня функції.
Змінні рівня класу оголошуються поза будь-якими функціями в класі JavaScript компонента та доступні для всіх функцій у класі. Зазвичай вони використовуються для зберігання даних, до яких потрібно отримати доступ з HTML або утримувати дані для маніпуляції компонентом протягом його життєвого циклу.
Наприклад, ви можете оголосити змінну рівня класу під назвою count, яка відстежує кількість натискань кнопки:
count = 0;
maxLimit = 10;
handleClick() {
if (this.count < this.maxLimit) {
this.count++;
}
}
У цьому прикладі змінну count оголошено поза функцією handleClick, тому функція може отримати до неї доступ і оновити її лише посилаючись через this. . Кожного разу, коли викликається функція handleClick (наприклад, коли натискається кнопка), змінна лічильника збільшується. Та перевіряється чи ми не досягли вказаного нами ліміту зі збільшення числа.
З іншого боку, змінні функціонального рівня оголошуються всередині функції й доступні лише в цій функції. Зазвичай вони використовуються для зберігання тимчасових або проміжних даних, які потрібні лише в контексті цієї функції. Наприклад, ви можете оголосити змінну функціонального рівня під назвою isValid, яка перевіряє, чи дійсний введений користувачем код:
У цьому прикладі змінна isValid оголошена всередині функції handleInputChange і використовується для перевірки дійсності введених користувачем даних. Змінна на рівні функції тут потрібна щоб виконати перевірку лише один раз та потім скористатись результатом двічі для IF кондиції та статусу помилки.
showError = false;
handleInputChange(event) {
let userInput = event.target.value;
let isValueInvalid = userInput.length > 10;
if (isValueInvalid ) {
console.log('You hit limit');
}
if (userInput.length === 0 || isValueInvalid) {
this.showError = true;
}
}
Також невід’ємною частиною роботи з LWC є різні анотації. Анотації є важливими елементами у розробці LWC в Salesforce. Вони використовуються для додавання метаданих до класів та методів компонентів, що допомагає забезпечити їхню правильну роботу.
У цій статті ми познайомимось лише з однієї з них – @track. Наступні анотації – такі як @api та @wire – ми розберемо у майбутніх статтях на тему LWC.
@track: Ця анотація використовується для відстежування змін у властивостях компонента. Вона дозволяє автоматично оновлювати розмітку компонента при зміні значень властивостей. Варто розуміти, що ви можете не використовувати анотацію @track для простих типів даних, таких як рядки, числа та булеві значення, оскільки вони вже автоматично відстежуються LWC. Анотація @track потрібна лише для відстежування змін в об’єктах та масивах, тому що зміни в цих типах даних не відстежуються автоматично.
У цьому прикладі ми додаємо елементи до масиву і для того, щоб наш компонент міг коректно відстежувати зміни у ньому ми додали анотацію трек.
Ось кілька порад для використання LWC:
- Зробіть ваші компоненти простими, використовуйте модульну конструкцію, розділяйте компоненти на менші частини, які можна багаторазово використовувати, та об’єднувати, щоб створити складніші інтерфейси.
- Хорошою практикою є компоненти, які виконують одну функцію і роблять це добре. Уникайте створення монолітних компонентів, які роблять занадто багато.
- Використовуйте шаблони дизайну, такі як PubSub, Decorator і Facade, щоб упорядкувати свій код і покращити можливість повторного використання коду.
- Використовуйте систему Salesforce Lightning Design System (SLDS), щоб переконатися, що ваші компоненти відповідають користувальницькому досвіду Salesforce й узгоджені з іншими компонентами Salesforce.
- Напишіть модульні тести для ваших компонентів, щоб переконатися, що вони поводяться належним чином, і виявити помилки на ранніх етапах циклу розробки.
Зараз давайте розпочнемо створення нашого першого LWC компонента та розглянемо на практиці усе те, про що говорили вище. Для цього розробимо просту форму для додавання простих чисел.
<template>
<section class="main">
<h1>Number sum form</h1>
<div class="twoColumnStyle">
<fieldset>
<div class="threeColumnStyle">
<lightning-input
value={firstNumber}
label="First Number"
onchange={handleInput}
data-name="firstNumber">
</lightning-input>
<lightning-input
value={secondNumber}
label="Second Number"
onchange={handleInput}
data-name="secondNumber">
</lightning-input>
<lightning-button
label="Sum"
onclick={handleButtonClick}>
</lightning-button>
</div>
<div class="oneColumnStyle">
<lightning-input
value={result}
label="Result">
</lightning-input>
</div>
</fieldset>
<section>
<template lwc:if={isHistoryExist}>
<h2>History of operations</h2>
<template for:each={operationsHistory} for:item="historyRecord">
<div key={historyRecord.value} class="borderStyle">
{historyRecord.firstNumber} + {historyRecord.secondNumber} = {historyRecord.value}
</div>
</template>
</template>
</section>
</div>
</section>
</template>
Для початку давайте створимо розмітку для нашого компонента.
На ній ми можемо побачити 2 поля введення чисел, кнопку запуску калькуляції, поле для відображення результатів калькуляції та блок з історією операцій, які вже відбулись.
Блок з історією ітерується по масиву об’єктів, у яких ми зберігаємо потрібні для нас дані (більш детально про ітерацію у LWC ми поговоримо в наступних статтях про LWC).
Також там ми можемо побачити LWC:IF, який не покаже цей блок, якщо історія є пустою.
З моментів, на які варто звернути увагу – це додавання дата нейму для полів введення, щоб надалі використати їх у JS для отримання даних.
import { LightningElement, track } from 'lwc';
export default class SimpleNumberSumForm extends LightningElement {
@track operationsHistory = [];
firstNumber;
secondNumber;
result;
connectedCallback() {
// One of common uses of connectedCallback is calling Apex method to retrieve data from SF
// We will show examples of this usage in a future video
}
errorCallback(error, stack) {
console.log(error, stack);
}
handleInput(event) {
this[event.target.dataset.name] = event.target.value;
}
handleButtonClick() {
this.result = parseInt(this.firstNumber) + parseInt(this.secondNumber);
if (!Number.isNaN(this.result)) {
let history = JSON.parse(JSON.stringify(this.operationsHistory));
history.push({firstNumber: this.firstNumber, secondNumber: this.secondNumber, value: this.result});
this.operationsHistory = history;
}
}
get isHistoryExist() {
return this.operationsHistory.length >= 1;
}
}
Тепер перейдемо до нашого JS класу. Для початку ми імпортували анотацію трек. Оголосили масив на рівні класу з цією анотацією для зберігання історії. В цьому випадку анотація потрібна, щоб наш LWC компонент реагував на зміни у масиві та дозволяв ітерації відпрацьовувати з урахуванням останніх змін у ньому.
Далі ми оголосили дві змінні для зберігання введення користувача та змінну для збереження результатів калькуляцій.
Далі на черзі - connectedCallback. Для прикладу у цьому компоненті ми можемо використати цей хук для ініціалізації історії попередніх операцій з Salesforce за допомогою Apex. Ми не будемо зараз зупинятись на цьому, оскільки у майбутніх статтях серії, присвячених LWC, ми будемо розбирати можливі способи комунікації LWC з Apex та покажемо цей випадок на прикладі.
Далі ми бачимо errorCallback для відображення помилок та їхнього stack trace у консолі.
Також ми додали 2 функції: перша handleInput викликається коли користувач вводить або змінює дані у полі. В цій функції ми отримуємо івент, у якому міститься значення, яке ввів користувач, та ім’я поля, яке саме надіслало цей івент. Оскільки попередньо ми додали дата нейм теги до наших інпут полів на html, ця конструкція дозволить нам використовувати одну функцію для присвоєння для усіх наших інпутів, тому що у дата нейм тег ми присвоїли те ж ім’я, що й у змінній на рівні класу, яка зберігає ці дані. Це динамічне звернення можна також замінити на звичайну конструкцію this крапка та змінна, до якої ми звертаємось.
Далі в нас є функція, яка спрацьовує на натискання кнопки. Вона бере перше та друге число, додає їх та зберігає у результаті. Далі, якщо це число є коректним, ми додаємо його в історію операцій, яка буде показана на сторінці.
Останнім ми бачимо геттер, який перевіряє чи довжина масиву історії операції не пуста для нашої кондиції LWC:IF, яка буде відображати історію.
Отже, Lightning Web Components - це сучасна, стандартна модель програмування для створення багаторазових компонентів інтерфейсу користувача, які працюють нативно в Salesforce. Дотримуючись рекомендацій та кращих практик Salesforce, ви можете створювати компоненти високої якості, масштабовані, безпечні та легкі у підтримці. Завдяки потужним базовим інструментом, LWC дозволяє легко керувати станом та життєвим циклом компонентів, а його модульна архітектура дозволяє створювати складні інтерфейси, комбінуючи прості, багаторазові компоненти. У наступній статті ми поговоримо про базові принципи розмітки у LWC. Розглянемо які можливості та особливості нам надає використання LWC для розробки наших додатків для Salesforce.