Работа с событиями
Подписка на событие
Для подписки на событие используют директиву on
. Можно подписаться на события элементов шаблона:
- DOM-элемент;
- контрол;
- шаблон, встроенный директивой ws:partial.
В следующем примере показана подписка на нативное событие DOM-элемента.
<!-- WML -->
<div>
<button on:click="clickHandler()">Нажми на меня</button>
</div>
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
clickHandler: function() {
alert('В элементе button произошло событие click.').
}
});
return ModuleClass;
});
В следующем примере показана подписка на событие контрола.
<!-- WML -->
<div>
<My.Button on:myEvent="myHandler()" />
</div>
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
myHandler: function() {
alert('В контроле произошло событие myEvent.').
}
});
return ModuleClass;
});
В следующем примере показана подписка на событие шаблона.
<!-- WML -->
<div>
<ws:partial template="wml!My/Template" on:someEvent="simpleHandler()" />
</div>
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
simpleHandler: function() {
alert('В шаблоне произошло событие someEvent.').
}
});
return ModuleClass;
});
Директива on
имеет следующую форму записи:
on:<событие>="<обработчик>(<параметры>)"
В Wasaby отказались от динамической подписки на события. Применяется только декларативная форма, благодаря которой:
- проще отслеживать созданные обработчики;
- эффективнее тестировать функционал;
- выполняется автоматическая отписка от события.
Пример:
Не стоит подписываться нативно, т.к. при синхронизации верстки элемент может быть пересоздан и подписка пропадет, оставив за собой утечку.define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) { var ModuleClass = Base.Control.extend({ _template: template, _afterMount: function() { this._children.myDiv.addEventListener('click', function() { // some work }); } }); return ModuleClass; });
В шаблоне можно подписаться только на события, которые происходят в элементах шаблона. При публикации события подписаться на него можно только в родительском контроле.
Передача параметров события из шаблона
В шаблоне параметр обрабатывается как синтаксическая конструкция. Результат её вычисления передаётся в аргумент обработчика.
<!-- Demo/Template.wml -->
<div>
<button on:click="clickHandler('Сейчас', getCurrentYear(), myUnit)">Нажми на меня</button>
</div>
// Module.js
define("Demo/Module", ["UI/Base", "wml!Demo/Template"], function(Base,template) {
var ModuleClass = Base.Control.extend({
_template: template,
myUnit: "год",
clickHandler: function(e, msg, date, unit) {
alert(msg + " " + date + " " + unit);
},
getCurrentYear: function() {
return new Date().getFullYear();
}
});
return ModuleClass;
});
См. демо-пример на CodeSandbox
Обработчик события
Обработчик события описывается как метод в рамках класса контрола (см. Создание класса контрола). В теле обработчика переменная this
содержит экземпляр класса контрола.
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
count: 0,
clickHandler: function() {
this.count++;
}
});
return ModuleClass;
});
<!-- WML -->
<div>
Counter: <button on:click="clickHandler()">{{ count }}</button>
</div>
См. демо-пример на CodeSandbox
При разработке контрола на TypeScript рекомендуется придерживаться следующих правил при создании обработчика.
В качестве обработчика события можно указать метод из дочернего контрола. Для этого дочерний контрол добавляют в шаблон и задают ему имя в атрибуте name
. При указании имени обработчика используют такую форму записи <значение атрибута name>.<имя обработчика>(<параметры>)
<!-- WML -->
<div>
<My.Other.Control name="otherControl">
<button on:click="otherControl.clickHandler()">Нажми на меня</button>
</div>
// JavaScript
define("My/Other/Control", ["UI/Base",... ], function(Base, ...) {
var ModuleClass = Base.Control.extend({
...
clickHandler: function() {
alert('В элементе button произошло событие click.').
}
});
return ModuleClass;
});
Дескриптор события
В обработчик первым аргументом всегда приходит дескриптор события
(экземпляр класса Vdom/Vdom:SyntheticEvent
). Он содержит детальную информацию о событии, для доступа к которой можно обратиться к следующим свойствам:
nativeEvent
— содержимое этого свойства зависит от типа события. Для нативного события содержит поля интерфейса Event. Принимаетnull
, если событие публикуется контролом.result
— значение, возвращенное из предыдущего обработчика, который выполнен на то же событие.stopped
— признак, что не происходит дальнейшее всплытие события по цепочке.target
— DOM-элемент, в котором произошло событие.type
— имя события.
В примере ниже показана работа с обоими типами событий:
<!-- WML -->
<div>
<button on:click="clickHandler()">Нажми на меня</button>
<My.TextBox on:valueChanged="myHandler()" />
</div>
// JavaScript
define("My/Module", ["UI/Base", "wml!My/Template"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
clickHandler: function(e) {
console.log(e.nativeEvent.clientX)
},
myHandler: function(e) {
console.log(e.target)
}
});
return ModuleClass;
});
Начиная со второго аргумента доступны параметры, переданные из шаблона или из модуля.
Приоритет обработки параметров события
В аргументах обработчика события сначала доступны параметры из шаблона, а далее — параметры из модуля, переданные в момент публикации.
В следующем примере создана подписка на событие myEvent
.
// JavaScript
define("My/Button", ["UI/Base", "wml!My/Button"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
startValue: 10,
endValue: 20,
_afterMount: function() {
this._notify("myEvent", [this.startValue, this.endValue]);
}
});
return ModuleClass;
});
<!-- WML -->
<div>
<My.Button on:myEvent="myHandler(newNumber)" />
</div>
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
newNumber: 30,
myHandler: function(e, arg1, arg2, arg3){
console.log(arg1) // 30
console.log(arg2) // 10
console.log(arg3) // 20
}
});
return ModuleClass;
});
Публикация пользовательского события
Для публикации события в контроле вызывают метод _notify()
. Первый аргумент метода — обязательный, он принимает имя события.
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
_afterMount: function() {
this._notify("myEvent");
}
});
return ModuleClass;
});
События не публикуют в хуке _beforeMount(), т.к. в этой точке жизненного цикла контрол не инициализирован.
Передача параметров события из модуля
Во втором аргументе метода _notify()
в массиве можно передать параметры. Они будут доступны в аргументах обработчика события.
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
startValue: 10,
endValue: 20,
_afterMount: function() {
this._notify("myEvent", [this.startValue, this.endValue]);
}
});
return ModuleClass;
});
Всплываемость
При публикации события его можно сделать всплываемым. Такое событие будет передаваться из контрола (источника события) вверх по всей цепочке родительских элементов шаблона. Данное поведение можно использовать для перехвата события и дальнейшей обработки.
Для этого следует в методе _notify()
передать третьим аргументом значение {bubbling: true}
.
// JavaScript
define("My/Control", ["UI/Base", "wml!My/Control"], function(Base, template) {
var ModuleClass = Base.Control.extend({
_template: template,
startValue: 10,
endValue: 20,
_afterMount: function() {
this._notify("myEvent", [], {bubbling: true});
}
});
return ModuleClass;
});
При обработке всплываемых событий есть особенности. Более подробно они будут рассмотрены в отдельной статье, потому что для их понимания необходимо предварительно познакомиться с контролами высшего порядка.