Свойства
Обзор
LitElement управляет объявленными свойствами и соответствующими им атрибутами. По умолчанию LitElement:
- Планирует обновление элемента при изменении любого объявленного свойства.
- Сохраняет значения экземпляра для объявленных свойств. Применяет значения свойств, установленные до регистрации браузером определения пользовательского элемента.
- Создаёт наблюдаемый (но не отражаемый) атрибут с именем каждого свойства, приведённым к нижнему регистру.
- Обрабатывает преобразование атрибутов для свойств, объявленных как типы
String,Number,Boolean,ArrayиObject. - Использует прямое сравнение (
oldValue !== newValue) для проверки изменений свойств. - Применяет любые параметры свойств и методы доступа, объявленные суперклассом.
Не забудьте объявить все свойства, которыми должен управлять LitElement. Чтобы применялись описанные выше возможности свойств, необходимо объявить свойство.
Объявление свойств
Объявляйте свойства элемента с помощью статического поля properties или декораторов:
Поле properties
static get properties() {
return {
propertyName: options
};
}
Декоратор (требуется TypeScript или Babel)
export class MyElement extends LitElement {
@property(options)
propertyName;
В обоих случаях можно передать объект параметров, чтобы настроить возможности свойства.
Параметры свойств
Объект параметров может содержать следующие свойства:
attributeСвязано ли свойство с атрибутом, или пользовательское имя связанного атрибута. По умолчанию: true. См. раздел Настройка наблюдаемых атрибутов. Если
attributeимеет значение false, параметрыconverter,reflectиtypeигнорируются.converterПользовательский преобразователь для преобразования между свойствами и атрибутами. Если он не указан, используется преобразователь атрибутов по умолчанию.
hasChangedФункция, принимающая
oldValueиnewValueи возвращающая логическое значение, которое указывает, изменилось ли свойство при присваивании. Если она не указана, LitElement использует проверку на строгое неравенство (newValue !== oldValue), чтобы определить, изменилось ли значение свойства.noAccessorУстановите значение true, чтобы не создавать метод доступа к свойству по умолчанию. По умолчанию: false.
reflectОтражается ли значение свойства обратно в связанный атрибут. По умолчанию: false. См. раздел Настройка отражаемых атрибутов.
typeПодсказка типа для преобразования между свойствами и атрибутами. Эта подсказка используется преобразователем атрибутов LitElement по умолчанию и игнорируется, если задано
converter. Еслиtypeне указано, поведение такое же, как уtype: String. См. раздел Использование преобразователя атрибутов LitElement по умолчанию.- Вызывается сеттер свойства.
- Сеттер вызывает функцию
hasChangedсвойства. ФункцияhasChangedпринимает старое и новое значения свойства и возвращает true, если изменение должно запускать обновление. (ФункцияhasChangedпо умолчанию проверяет строгое неравенство (oldValue !== newValue), чтобы определить, изменилось ли свойство.) - Если
hasChangedвозвращает true, сеттер вызываетrequestUpdate, чтобы запланировать обновление. Само обновление выполняется асинхронно, поэтому одновременное обновление нескольких свойств приводит только к одному обновлению. - Вызывается метод
updateкомпонента, который отражает изменённые свойства в атрибуты и повторно отрисовывает шаблоны компонента. Чтобы наблюдать за атрибутом (устанавливать свойство на основе атрибута), значение атрибута необходимо преобразовать из строки в тип свойства.
Чтобы отражать атрибут (устанавливать атрибут на основе свойства), значение свойства необходимо преобразовать в строку.
- Для строк: если атрибут задан, свойству присваивается значение атрибута.
- Для чисел: если атрибут задан, свойству присваивается
Number(attributeValue). - Для логических значений, если атрибут:
- не
null, свойству присваиваетсяtrue. -
nullилиundefined, свойству присваиваетсяfalse.
- не
- Для объектов и массивов, если атрибут:
- Задан, значением свойства становится
JSON.parse(attributeValue).
- Задан, значением свойства становится
- Для строк, если свойство:
-
null, атрибут удаляется. -
undefined, атрибут не изменяется. - Задано и не
null, атрибуту присваивается значение свойства.
-
- Для чисел, если свойство:
-
null, атрибут удаляется. -
undefined, атрибут не изменяется. - Задано и не
null, атрибуту присваивается значение свойства.
-
- Для логических значений, если свойство:
- истинное, атрибут создаётся.
- ложное, атрибут удаляется.
- Для объектов и массивов, если свойство:
-
nullилиundefined, атрибут удаляется. - Задано и не
null, значением атрибута становитсяJSON.stringify(propertyValue).
-
Если
toAttributeвозвращаетnull, атрибут удаляется.Если
toAttributeвозвращаетundefined, атрибут не изменяется.Если
toAttributeвозвращаетnull, атрибут удаляется.Если
toAttributeвозвращаетundefined, атрибут не изменяется.Если сама функция
toAttributeне определена, атрибуту присваивается значение свойства без преобразования.-
hasChangedвозвращаетtrue, еслиnewVal !== oldVal. -
hasChangedвозвращаетfalse, если новое и старое значения равныNaN.
Пустой объект параметров эквивалентен указанию значений по умолчанию для всех параметров.
Объект параметров под другим именем. В этом руководстве используется описательный термин «объект параметров». На практике объект параметров является экземпляром PropertyDeclaration, поэтому именно это имя отображается в IDE или в справочнике API. В обоих случаях речь идёт об объекте, который определяет набор параметров.
Объявление свойств в статическом поле properties
Чтобы объявить свойства в статическом поле properties:
static get properties() {
return {
greeting: {type: String},
data: {attribute: false},
items: {}
};
}
Пустой объект параметров эквивалентен указанию значений по умолчанию для всех параметров.
Объявленные свойства инициализируются как стандартные поля класса — в конструкторе или с помощью инициализатора поля при использовании декораторов.
Пример: объявление свойств с помощью статического поля properties
import {LitElement, html} from 'lit-element';
class MyElement extends LitElement {
static get properties() {
return {
greeting: {type: String},
data: {attribute: false},
items: {type: Array},
};
}
constructor() {
super();
this.greeting = 'Hello';
this.data = {name: 'Cora'};
this.items = [1, 2, 3];
}
render() {
return html`
<p>${this.greeting} ${this.data.name}.</p>
<p>You have ${this.items.length} items.</p>
`;
}
}
customElements.define('my-element', MyElement);
Объявление свойств с помощью декораторов
Используйте декоратор @property для объявления свойств (вместо статического поля properties).
@property({type: String})
mode = 'auto';
@property()
data = {};
Аргумент декоратора @property — это объект параметров. Если аргумент не указан, это эквивалентно указанию значений по умолчанию для всех параметров.
Использование декораторов. Декораторы — предлагаемая возможность JavaScript, поэтому для их использования понадобится транспилятор, например Babel, или компилятор TypeScript. Подробнее см. в разделе Использование декораторов.
Также существует декоратор @internalProperty для приватных или защищённых свойств, которые должны запускать цикл обновления. На свойства, объявленные с помощью @internalProperty, не следует ссылаться за пределами компонента.
@internalProperty() protected active = false;
Декоратор @internalProperty автоматически устанавливает attribute в false; для внутреннего свойства можно указать только функцию hasChanged.
Декоратор @internalProperty может подсказать минификатору кода, что имя свойства можно изменить при минификации.
Пример: объявление свойств с помощью декораторов
import {LitElement, html, customElement, property} from 'lit-element';
@customElement('my-element')
export class MyElement extends LitElement {
@property()
greeting = 'Hello';
@property({attribute: false})
data = {name: 'Cora'};
@property({type: Array})
items = [1, 2, 3];
render() {
return html`
<p>${this.greeting} ${this.data.name}.</p>
<p>You have ${this.items.length} items.</p>
`;
}
}
Что происходит при изменении свойств
Изменение свойства может запустить асинхронный цикл обновления, в результате которого компонент повторно отрисовывает свой шаблон.
При изменении свойства происходит следующая последовательность действий:
Существует множество способов подключиться к жизненному циклу обновления и изменить его. Дополнительные сведения см. в разделе Жизненный цикл.
Инициализация значений свойств
Обычно значения свойств инициализируют в конструкторе элемента.
При использовании декораторов значение свойства можно инициализировать прямо в объявлении (это эквивалентно установке значения в конструкторе).
Если вычисление значения свойства требует значительных ресурсов и это значение не нужно для первоначальной отрисовки компонента, инициализацию можно отложить. Такая ситуация встречается довольно редко.
Инициализация значений свойств в конструкторе элемента
Если вы реализуете поле static properties, инициализируйте значения свойств в конструкторе элемента:
static get properties() { return { /* Property declarations */ }; }
constructor() {
// Always call super() first
super();
// Initialize properties
this.greeting = 'Hello';
}
Не забудьте сначала вызвать super() в конструкторе, иначе элемент вообще не будет отображаться.
Пример: инициализация значений свойств в конструкторе элемента
Инициализация значений свойств при использовании декораторов
При использовании декоратора @property можно инициализировать свойство прямо в объявлении:
@property({type : String})
greeting = 'Hello';
Пример: инициализация значений свойств при использовании декораторов
Настройка атрибутов
Преобразование между свойствами и атрибутами
Свойства элемента могут иметь любой тип, тогда как атрибуты всегда являются строками. Это влияет на наблюдаемые атрибуты и отражаемые атрибуты свойств, не являющихся строками:
Использование преобразователя по умолчанию
В LitElement есть преобразователь по умолчанию, который обрабатывает свойства типов String, Number, Boolean, Array и Object.
Чтобы использовать преобразователь по умолчанию, укажите параметр type в объявлении свойства:
// Use LitElement's default converter
prop1: { type: String },
prop2: { type: Number },
prop3: { type: Boolean },
prop4: { type: Array },
prop5: { type: Object }
Ниже описано, как преобразователь по умолчанию обрабатывает преобразование каждого типа.
Преобразование атрибута в свойство
Преобразование свойства в атрибут
Пример: использование преобразователя по умолчанию
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
static get properties() { return {
prop1: { type: String, reflect: true },
prop2: { type: Number, reflect: true },
prop3: { type: Boolean, reflect: true },
prop4: { type: Array, reflect: true },
prop5: { type: Object, reflect: true }
};}
constructor() {
super();
this.prop1 = '';
this.prop2 = 0;
this.prop3 = false;
this.prop4 = [];
this.prop5 = { };
}
attributeChangedCallback(name, oldVal, newVal) {
console.log('attribute change: ', name, newVal);
super.attributeChangedCallback(name, oldVal, newVal);
}
render() {
return html`
<p>prop1 ${this.prop1}</p>
<p>prop2 ${this.prop2}</p>
<p>prop3 ${this.prop3}</p>
<p>prop4: ${this.prop4.map((item, index) =>
html`<span>[${index}]:${item} </span>`)}
</p>
<p>prop5:
${Object.keys(this.prop5).map(item =>
html`<span>${item}: ${this.prop5[item]} </span>`)}
</p>
<button @click="${this.changeProperties}">change properties</button>
<button @click="${this.changeAttributes}">change attributes</button>
`;
}
changeAttributes() {
let randy = Math.floor(Math.random()*10);
let myBool = this.getAttribute('prop3');
this.setAttribute('prop1', randy.toString());
this.setAttribute('prop2', randy.toString());
this.setAttribute('prop3', myBool? '' : null);
this.setAttribute('prop4', JSON.stringify([...this.prop4, randy]));
this.setAttribute('prop5',
JSON.stringify(Object.assign({}, this.prop5, {[randy]: randy})));
this.requestUpdate();
}
changeProperties() {
let randy = Math.floor(Math.random()*10);
let myBool = this.prop3;
this.prop1 = randy.toString();
this.prop2 = randy;
this.prop3 = !myBool;
this.prop4 = [...this.prop4, randy];
this.prop5 = Object.assign({}, this.prop5, {[randy]: randy});
}
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
}
}
customElements.define('my-element', MyElement);
Настройка пользовательского преобразователя
В объявлении свойства можно указать пользовательский преобразователь свойств с помощью параметра converter:
myProp: {
converter: // Custom property converter
}
converter может быть объектом или функцией. Если это объект, он может содержать ключи fromAttribute и toAttribute:
prop1: {
converter: {
fromAttribute: (value, type) => {
// `value` is a string
// Convert it to a value of type `type` and return it
},
toAttribute: (value, type) => {
// `value` is of type `type`
// Convert it to a string and return it
}
}
}
Если converter — функция, она используется вместо fromAttribute:
myProp: {
converter: (value, type) => {
// `value` is a string
// Convert it to a value of type `type` and return it
}
}
Если для отражаемого атрибута не задана функция toAttribute, атрибуту присваивается значение свойства без преобразования.
Во время обновления:
Пример: настройка пользовательского преобразователя
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
static get properties() { return {
myProp: {
reflect: true,
converter: {
toAttribute(value) {
console.log('myProp\'s toAttribute.');
console.log('Processing:', value, typeof(value));
let retVal = String(value);
console.log('Returning:', retVal, typeof(retVal));
return retVal;
},
fromAttribute(value) {
console.log('myProp\'s fromAttribute.');
console.log('Processing:', value, typeof(value));
let retVal = Number(value);
console.log('Returning:', retVal, typeof(retVal));
return retVal;
}
}
},
theProp: {
reflect: true,
converter(value) {
console.log('theProp\'s converter.');
console.log('Processing:', value, typeof(value));
let retVal = Number(value);
console.log('Returning:', retVal, typeof(retVal));
return retVal;
}},
};}
constructor() {
super();
this.myProp = 'myProp';
this.theProp = 'theProp';
}
attributeChangedCallback(name, oldval, newval) {
// console.log('attribute change: ', name, newval);
super.attributeChangedCallback(name, oldval, newval);
}
render() {
return html`
<p>myProp ${this.myProp}</p>
<p>theProp ${this.theProp}</p>
<button @click="${this.changeProperties}">change properties</button>
<button @click="${this.changeAttributes}">change attributes</button>
`;
}
changeAttributes() {
let randomString = Math.floor(Math.random()*100).toString();
this.setAttribute('myprop', 'myprop ' + randomString);
this.setAttribute('theprop', 'theprop ' + randomString);
this.requestUpdate();
}
changeProperties() {
let randomString = Math.floor(Math.random()*100).toString();
this.myProp='myProp ' + randomString;
this.theProp='theProp ' + randomString;
}
}
customElements.define('my-element', MyElement);
Настройка наблюдаемых атрибутов
Наблюдаемый атрибут вызывает callback-функцию API пользовательских элементов attributeChangedCallback при каждом изменении. По умолчанию, когда атрибут вызывает эту callback-функцию, LitElement устанавливает значение свойства на основе атрибута с помощью функции fromAttribute этого свойства. Дополнительные сведения см. в разделе Преобразование между свойствами и атрибутами.
По умолчанию LitElement создаёт соответствующий наблюдаемый атрибут для каждого объявленного свойства. Имя наблюдаемого атрибута совпадает с именем свойства, приведённым к нижнему регистру:
// observed attribute name is "myprop"
myProp: { type: Number }
Чтобы создать наблюдаемый атрибут с другим именем, задайте для attribute строковое значение:
// Observed attribute will be called my-prop
myProp: { attribute: 'my-prop' }
Чтобы не создавать для свойства наблюдаемый атрибут, задайте для attribute значение false. Свойство не будет инициализировано из атрибутов в разметке, а изменения атрибутов не будут на него влиять.
// No observed attribute for this property
myProp: { attribute: false }
Наблюдаемый атрибут можно использовать, чтобы задать начальное значение свойства с помощью разметки. См. раздел Инициализация свойств с помощью атрибутов в разметке.
Пример: настройка наблюдаемых атрибутов
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
static get properties() { return {
myProp: { attribute: true },
theProp: { attribute: false },
otherProp: { attribute: 'other-prop' },
};}
constructor() {
super();
this.myProp = 'myProp';
this.theProp = 'theProp';
this.otherProp = 'otherProp';
}
attributeChangedCallback(name, oldval, newval) {
console.log('attribute change: ', name, newval);
super.attributeChangedCallback(name, oldval, newval);
}
render() {
return html`
<p>myProp ${this.myProp}</p>
<p>theProp ${this.theProp}</p>
<p>otherProp ${this.otherProp}</p>
<button @click="${this.changeAttributes}">change attributes</button>
`;
}
changeAttributes() {
let randomString = Math.floor(Math.random()*100).toString();
this.setAttribute('myprop', 'myprop ' + randomString);
this.setAttribute('theprop', 'theprop ' + randomString);
this.setAttribute('other-prop', 'other-prop ' + randomString);
this.requestUpdate();
}
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
console.log(`${propName} changed. oldValue: ${oldValue}`);
});
}
}
customElements.define('my-element', MyElement);
Настройка отражаемых атрибутов
Можно настроить свойство так, чтобы при каждом его изменении значение отражалось в его наблюдаемом атрибуте. Например:
// Value of property "myProp" will reflect to attribute "myprop"
myProp: {reflect: true}
При изменении свойства LitElement использует функцию toAttribute преобразователя этого свойства, чтобы задать значение атрибута на основе нового значения свойства.
Во время обновления LitElement отслеживает состояние отражения. LitElement отслеживает состояние, чтобы избежать бесконечного цикла изменений между свойством и наблюдаемым отражаемым атрибутом.
Пример: настройка отражаемых атрибутов
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
static get properties() { return {
myProp: { reflect: true }
};}
constructor() {
super();
this.myProp='myProp';
}
attributeChangedCallback(name, oldval, newval) {
console.log('attribute change: ', newval);
super.attributeChangedCallback(name, oldval, newval);
}
render() {
return html`
<p>${this.myProp}</p>
<button @click="${this.changeProperty}">change property</button>
`;
}
changeProperty() {
let randomString = Math.floor(Math.random()*100).toString();
this.myProp='myProp ' + randomString;
}
}
customElements.define('my-element', MyElement);
Установка значений свойств из атрибутов в разметке
Если для свойства настроен параметр attribute: true (значение по умолчанию), пользователи могут задавать значения свойств с помощью наблюдаемых атрибутов в статической разметке:
index.html
<my-element
mystring="hello world"
mynumber="5"
mybool
myobj='{"stuff":"hi"}'
myarray='[1,2,3,4]'></my-element>
Дополнительные сведения о настройке инициализации из атрибутов см. в разделах Наблюдаемые атрибуты и Преобразование между свойствами и атрибутами.
Атрибуты и привязки свойств. Задание статического значения атрибута — не то же самое, что привязка к свойству. См. раздел Привязка к свойству.
Настройка методов доступа к свойствам
По умолчанию LitElement создаёт пару геттера и сеттера для всех объявленных свойств. Сеттер вызывается при каждой установке значения свойства:
// Declare a property
static get properties() { return { myProp: { type: String } }; }
...
// Later, set the property
this.myProp = 'hi'; // invokes myProp's generated property accessor
Сгенерированные методы доступа автоматически вызывают requestUpdate, запуская обновление, если оно ещё не началось.
Создание собственных методов доступа к свойствам
Чтобы задать способ получения и установки значения свойства, можно определить собственную пару геттера и сеттера. Например:
static get properties() { return { myProp: { type: String } }; }
set myProp(value) {
// Implement setter logic here...
// retrieve the old property value and store the new one
this.requestUpdate('myProp', oldValue);
}
get myProp() { ... }
...
// Later, set the property
this.myProp = 'hi'; // Invokes your setter
Если класс определяет собственные методы доступа к свойству, LitElement не заменяет их сгенерированными методами. Если класс не определяет методы доступа к свойству, LitElement создаёт их, даже если свойство или методы доступа к нему определены в суперклассе.
Сеттеры, автоматически создаваемые LitElement, вызывают requestUpdate. Если вы пишете собственный сеттер, необходимо вручную вызвать requestUpdate, передав имя свойства и его старое значение.
Пример
Обычно значение свойства сохраняют в приватном свойстве, к которому обращаются только внутри компонента. В этом примере для обозначения приватного свойства используется подчёркивание в начале имени (_prop); также можно использовать ключевые слова TypeScript private или protected.
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
static get properties() {
return { prop: { type: Number } };
}
set prop(val) {
let oldVal = this._prop;
this._prop = Math.floor(val);
this.requestUpdate('prop', oldVal);
}
get prop() { return this._prop; }
constructor() {
super();
this._prop = 0;
}
render() {
return html`
<p>prop: ${this.prop}</p>
<button @click="${() => { this.prop = Math.random()*10; }}">
change prop
</button>
`;
}
}
customElements.define('my-element', MyElement);
Чтобы использовать собственный метод доступа к свойству вместе с декоратором @property, можно поместить декоратор над геттером:
private _myProp: string = '';
@property({ type: String })
get myProp(): string {
return this._myProp;
}
set myProp(value: string) {
const oldValue = this._myProp;
this._myProp = value;
this.requestUpdate('myProp', oldValue);
}
Запрет генерации методов доступа к свойствам в LitElement
В редких случаях подклассу может потребоваться изменить или добавить параметры свойства, существующего в его суперклассе.
Чтобы LitElement не создавал метод доступа к свойству, который перезаписал бы метод, определённый в суперклассе, задайте для noAccessor значение true в объявлении свойства:
static get properties() {
return { myProp: { type: Number, noAccessor: true } };
}
При определении собственных методов доступа задавать noAccessor не нужно.
Пример
Элемент-подкласс
import { SuperElement } from './super-element.js';
class SubElement extends SuperElement {
static get properties() {
return { prop: { reflect: true, noAccessor: true } };
}
}
customElements.define('sub-element', SubElement);
Настройка изменений свойств
У всех объявленных свойств есть функция hasChanged, которая вызывается при установке значения свойства.
hasChanged сравнивает старое и новое значения свойства и определяет, изменилось ли свойство. Если hasChanged возвращает true, LitElement запускает обновление элемента, если оно ещё не запланировано. Дополнительные сведения о работе обновлений см. в документации по жизненному циклу обновления элемента.
По умолчанию:
Чтобы настроить hasChanged для свойства, укажите его в качестве параметра свойства:
myProp: { hasChanged(newVal, oldVal) {
// compare newVal and oldVal
// return `true` if an update should proceed
}}
hasChanged может вызываться не при каждом изменении. Если функция hasChanged свойства один раз возвращает true, она не будет вызвана снова до следующего обновления, даже если свойство изменится несколько раз. Если нужно получать уведомление при каждой установке значения свойства, создайте для него собственный сеттер, как описано в разделе Создание собственных методов доступа к свойствам.
Пример: настройка изменений свойств
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
static get properties(){ return {
myProp: {
type: Number,
/**
* Compare myProp's new value with its old value.
*
* Only consider myProp to have changed if newVal is larger than
* oldVal.
*/
hasChanged(newVal, oldVal) {
if (newVal > oldVal) {
console.log(`${newVal} > ${oldVal}. hasChanged: true.`);
return true;
}
else {
console.log(`${newVal} <= ${oldVal}. hasChanged: false.`);
return false;
}
}
}};
}
constructor(){
super();
this.myProp = 1;
}
render(){
return html`
<p>${this.myProp}</p>
<button @click="${this.getNewVal}">get new value</button>
`;
}
updated(){
console.log('updated');
}
getNewVal(){
let newVal = Math.floor(Math.random()*10);
this.myProp = newVal;
}
}
customElements.define('my-element', MyElement);
© Google LLC
Licensed under the Creative Commons Attribution 3.0 Unported License.
https://lit.dev/docs/v1/components/properties/