Как обнаружить изменение атрибутов веб-компонента

Источник: «How to detect when attributes change on a Web Component»
Сегодня мы рассмотрим, как обнаружить и реагировать на изменение атрибутов веб-компонента.

Ранее мы рассмотрели жизненный цикл веб-компонента. Хотя веб-компоненты не обладают реактивностью данных, один из методов жизненного цикла, встроенный в API веб-компонента, может использоваться для обнаружения изменения атрибута вашего элемента.

Метод attributeChangedCallback()

Метод attributeChangedCallback() является частью жизненного цикла веб-компонента и запускается каждый раз, когда атрибут веб-компонента добавляется, удаляется или изменяется его значение.

Он принимает три аргумента: name (имя) изменяемого атрибута, его oldValue (старое значение) и newValue (новое значение).

/**
* Выполняется при изменении значения атрибута компонента
* @param {String} name Имя атрибута
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

attributeChangedCallback (name, oldValue, newValue) {
console.log('attribute changed', name, oldValue, newValue, this);
}

По соображениям производительности метод attributeChangedCallback() следит и реагирует только на те атрибуты, которые были указаны.

Для этого создаётся статическое свойство observedAttributes, значением которого является массив атрибутов для наблюдения.

Можно использовать любые атрибуты, включая нестандартные. В этом примере укажем веб-компоненту следить за изменениями атрибутов [text] и [pause].

// Определяем атрибуты для наблюдения
static observedAttributes = ['text', 'pause'];

/**
* Выполняется при изменении значения атрибута компонента
* @param {String} name Имя атрибута
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

attributeChangedCallback (name, oldValue, newValue) {
console.log('attribute changed', name, oldValue, newValue, this);
}

Теперь можно сделать что-то вроде этого…

let count = document.querySelector('wc-count');

// logs "attribute changed" "text" null "You clicked it: {{count}}"
count.setAttribute('text', 'You clicked it: {{count}}');

Но если изменить атрибут, которого нет в списке observedAttributes, ничего не произойдёт.

// Ничего не происходит
count.setAttribute('id', 'count-1234');

В этом примере данные выводятся в консоль. Можно включить панель Инспектор и перейти во вкладку консоль и увидеть результат выполнения скрипта там. Или перейти на сайт CodePen с этим примером.

See the Pen

Реагирование на изменение атрибутов веб-компонента

Теперь, когда мы обнаруживаем изменения атрибутов, можем реагировать на них.

Например, когда атрибут [text] добавляется или изменяется в элементе <wc-count>, можно обновить свойство this.text и перерисовать текст в this.button с обновлённым текстом.


/**
* Выполняется при изменении значения атрибута компонента
* @param {String} name Имя атрибута
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

attributeChangedCallback (name, oldValue, newValue) {

// Если атрибут [text], обновляем this.text и отображаем текст кнопки
if (name === 'text') {
this.text = newValue;
this.button.textContent = this.text.replace('{{count}}', this.count);
}

}

И, возможно, при добавлении атрибута [pause] мы перестанем считать клики и [disable] кнопку.


/**
* Выполняется при изменении значения атрибута компонента
* @param {String} name Имя атрибута
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

attributeChangedCallback (name, oldValue, newValue) {

// Если атрибут [text], обновляем this.text и отображаем текст кнопки
if (name === 'text') {
this.text = newValue;
this.button.textContent = this.text.replace('{{count}}', this.count);
}

// Если атрибут [pause] останавливаем отсчёт.
if (name === 'pause') {
this.button.removeEventListener('click', this);
this.button.setAttribute('disabled', '');
}

}

Когда атрибут [pause] будет удалён, возможно, нам захочется начать подсчёт заново.

Для этого проверяем значение параметра newValue. Если оно равно null, то атрибут был удалён. В противном случае он был добавлен.


/**
* Выполняется при изменении значения атрибута компонента
* @param {String} name Имя атрибута
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

attributeChangedCallback (name, oldValue, newValue) {

// Если атрибут [text], обновляем this.text и отображаем текст кнопки
if (name === 'text') {
this.text = newValue;
this.button.textContent = this.text.replace('{{count}}', this.count);
}

// Если атрибут [pause] останавливаем отсчёт.
if (name === 'pause') {
if (newValue === null) {
this.button.addEventListener('click', this);
this.button.removeAttribute('disabled');
} else {
this.button.removeEventListener('click', this);
this.button.setAttribute('disabled', '');
}
}

}

See the Pen

Организация кода

Если вы отслеживаете несколько атрибутов, функция attributeChangedCallback() может стать довольно громоздкой.

Чтобы было проще работать, я стараюсь абстрагировать код для обработки этих изменений в методах-обработчиках. Я добавляю к ним префикс handlechange*, где * — имя атрибута.


/**
* Обработка изменений атрибута [text]
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

handlechangetext (oldValue, newValue) {
this.text = newValue;
this.button.textContent = this.text.replace('{{count}}', this.count);
}

/**
* Обработка изменений атрибута [pause]
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

handlechangepause (oldValue, newValue) {
if (newValue === null) {
this.button.addEventListener('click', this);
this.button.removeAttribute('disabled');
} else {
this.button.removeEventListener('click', this);
this.button.setAttribute('disabled', '');
}
}

Затем, в методе attributeChangedCallback(), можно автоматически запустить нужный метод с помощью скобочной нотации и шаблонного литерала. Передав в него oldValue и newValue.

/**
* Выполняется при изменении значения атрибута компонента
* @param {String} name Имя атрибута
* @param {String} oldValue Старое значение атрибута
* @param {String} newValue Новое значение атрибута
*/

attributeChangedCallback (name, oldValue, newValue) {
this[`handlechange${name}`](oldValue, newValue);
}

See the Pen

Все статьи серии о Веб-Компонентах/Web Component

Дополнительные материалы

Предыдущая Статья

Методы жизненного цикла веб-компонента

Следующая Статья

Как заставить веб-компоненты общаться (часть 1)