События реализованы как в модуле "event" node js и браузера. Библиотека ui-organizer здесь ничего нового не добавляет. Она только реализует возможность описать эти события.
Каждый элемент управления генерирует события. Например, объект (элемент управления), который реализует интерфейс IElement генерирует следующие события. Все остальные интерфейсы являются наследниками IElement, поэтому все элементы управления также генерируют эти события.
Почему в интерфейсе IElement указаны методы onBeforeLoad, onAfterLoad и события
beforeLoad, afterLoad?
Потому что обработчики события могут быть разными и могут создаваться другими элементами, например:
/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/import'./index.html';import'ui-organizer/page.css';import'ui-organizer/style.css';import'ui-organizer/vars.css';/*Описание формы simpleForm*/importtype {IForm, IElement} from"ui-organizer";import { AppManager, Grouping, Flex, UILoadEvent} from"ui-organizer";exportvarform: IForm = <IForm>{type:'IForm',name:'simpleForm',flex:Flex.flexible,grouping:Grouping.vertical,elements: [ <IElement>{type:'IElement',name:'header',//Обработчик события afterLoad элемента headeronAfterLoad:asyncfunction (event: UILoadEvent, form: IForm, elem: IElement) {elem.value = 'Мое приложение'; } }, ],onBeforeLoad:asyncfunction (event: UILoadEvent, form: IForm, elem: IElement) {varheader: IElement = form.getElement('header');//Второй обработчик события afterLoad элемента headerheader.on("afterLoad", (_event, _form, _elem) => {_elem.addClass('header'); }) }}/*Инициализация приложения AppManager*/varGlobal: any = window;Global.AppManager = AppManager;Global.onpopstate = AppManager.onPopState;AppManager.init(document.querySelector('#app')) .then(async ()=>{AppManager.add([form]);AppManager.open('simpleForm', {}, undefined); })
В примере элемент IElement имеет обработчик события onAfterLoad со своей логикой. Форма добавляет к событию afterLoad элемента дополнительную логику.
Загрузка элементов управления, как и другие события, генерируемые элементами управления (например, установка данных onBeforeSetData, onAfterSetData или редактирование onBeforeEdit, onAfterEdit), вызываются последовательно согласно вложенности элементов. Т.е. сначала onBeforeLoad формы, потом onBeforeLoad вложенных в форму элементов, потом onBeforeLoad вложенных во вложенные элементы. Затем, когда достигнем самого последнего вложенного элемента вызываются onAfterLoad этого элемента, потом onAfterLoad элемента, содержащего предыдущий, и так до onAfterLoad формы.
Более подробно о событии afterLoad и beforeLoad смотрите в разделе 2.
События элементов управления можно разделить на три категории:
События, которые генерируются самими элементами управления(onBeforeLoad, onAfterLoad; onBeforeSetData, onAfterSetData; onBeforeEdit, onAfterEdit и др.).
События, которые генерируются в результате действий пользователя (click, dblClick; keydown, keyup; focus, blur и др).
События, которые добавил разработчик.
События, которые генерируются самими элементами управления, имеют в своем названии слова before и after. Т.е. вызываются до и после действия элемента как описано в разделе 1.1.. Такие события вызываются последовательно для каждого элемента в цепочке вложенных элементов управления. Обработчикам событий этой категории первым аргументом передается объект события. С помощью объекта события вы получаете доступ к данным события, а также можете остановить событие event.stop(). Например, при загрузке элемента в обработчке события onBeforeLoad вы можете остановить загрузку конкретного элемента, тогда событие onAfterLoad этого элемента не будет вызвано. И еще, в отличие от второй категории, событие элемента не связано с таким же событием вложенных элементов.
События, которые генерируются в результате действий пользователя, не имеют вызовов до и после. Т.е. событие еже произошло и нам надо его обработать, как описано в разделе 1.3.. Такие события также вызываются последовательно по всей цепочке вложенных элементов, но с тем обстоятельством, что событие произошло на самом нижнем элементе (например, click) и события всех вышестоящих элементов связаны с этим фактом. Обработчикам событий этой категории также первым аргументом передается объект события. С его помощью вы получаете доступ к данным события. а также можете остановить распространение событие event.stop() по аналогии с методом события MouseEvent.stopPropagation() браузера. Например, при click по элементу сначала click обрабатывает форма, затем вложенные элементы, затем искомый элемент по цепочке вложенности. На любом этапе вы можете остановить распространение этого события. Кроме остановки распространения события вы можете отключить предопределенные действия формы при возникновении таких событий event.preventDefault() (например, отключить навигацию по элементам управления клавишей Tab в событии keydown).
Еще одной особенностью событий этой категории является то, что событие сначала опускается от формы до последнего элемента в цепочке вложенности, потом поднимается от элемента к форме. По аналогии с capturing и babbling при click в браузере.
События, которые добавил разработчик, генерируются и обрабатываются по замыслу самого разработчика. Данные, передаваемые таким событиям, задаются также самим разработчиком, как описано в разделе1.4..
Пользователь может генерировать такие события как click, dblClick, keydown, keyup, focus, blur и др. Рассмотрим на примере объекта (элемент управления), который реализует интерфейс IElement событие click. Все остальные интерфейсы являются наследниками IElement, поэтому также предоставляют возможность обрабатывать это событие.
exportinterfaceIElement<T=IElementEvents> extendsStrictEventEmitter<EventEmitter, T>{onClick?(event: UIMouseEvent, form: IForm, elem: IElement, item: IElement): Promise<void>;/**другие методы обработчики событий */}exportinterfaceIElementEvents {click(event: UIMouseEvent, form: IForm, elem: IElement, item: IElement): void,/**другие события */}
Обработчики событий генерируемых пользователем вызываются последовательно по всей цепочке вложенных элементов:
/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/import'./index.html';import'ui-organizer/page.css';import'ui-organizer/style.css';import'ui-organizer/vars.css';/*Описание формы simpleForm*/importtype {IForm, IElement, IGroup} from"ui-organizer";import { AppManager, UIMouseEvent} from"ui-organizer";exportvarform: IForm = <IForm>{type:'IForm',name:'simpleForm',elements: [ <IGroup>{type:'IGroup',elements: [ <IElement>{type:'IElement',name:'simpleElement',caption:'Элемент',asynconClick(e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) {console.log(`Элемент. Клик по элементу ${elem.name} - погружение`); } }, ],asynconClick(e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) {console.log(`Группа знает, что кликнули по элементу ${e.targetElement.name} - погружение`); } }, ],asynconClick(e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) {console.log(`Форма знает, что кликнули по элементу ${e.targetElement.name} - погружение`); }}/*Инициализация приложения AppManager*/varGlobal: any = window;Global.AppManager = AppManager;Global.onpopstate = AppManager.onPopState;AppManager.init(document.querySelector('#app')) .then(async ()=>{AppManager.add([form]);AppManager.open('simpleForm', {data:"Данные"}); })
В приведенном примере, если кликнуть по элементу, то вызовутся все обработчики по очереди, от формы до элемента. В результате получим следующий вывод в консоли:
Если вы хотите, чтобы ваш элемент управления имел дополнительное событие someSetted. Для этого задайте интерфейс. Конечно, вы можете не создавать интерфейс, это и так будет работать, но задание интерфейса убережет вас от ошибок и сэкономит кучу времени в будущем.
Описание вашего элемента управления может быть следующим:
/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/import'./index.html';import'ui-organizer/page.css';import'ui-organizer/style.css';import'ui-organizer/vars.css';/*Описание формы simpleForm*/importtype {IForm, IElement, IElementEvents} from"ui-organizer";import { AppManager, UIForm, UILoadEvent } from"ui-organizer";//Описание пользовательского интерфейса наследника IElementexportinterfaceISomeElement<T=ISomeElementEvents> extendsIElement<T> {someProperty: string;setSomeProperty(val: string): void;}exportinterfaceISomeElementEventsextendsIElementEvents{someSetted(form: IForm, element: IElement): void,}//Описание элемента elem и формы form.exportvarelem: ISomeElement = <ISomeElement>{type:'IElement',name:'someElement',someProperty:undefined,setSomeProperty:function (val: string) {this.someProperty = val;this.emit("someSetted", this.form, this); }}exportletform: IForm = <IForm>{type:UIForm,name:'simpleForm',elements:[elem ],asynconAfterLoad(event: UILoadEvent, form: IForm, elem:IElement){letsomeElem: ISomeElement = form.getElement('someElement') asISomeElement;someElem.value = 'someElement';addListener(someElem);someElem.setSomeProperty('OK'); }}exportfunctionaddListener(elem:ISomeElement){elem.on('someSetted', async (form:IForm, elem:ISomeElement) => {console.log(`${elem.name}: someProperty установлен в ${elem.someProperty}.`); })}/*Инициализация приложения AppManager*/varGlobal: any = window;Global.AppManager = AppManager;Global.onpopstate = AppManager.onPopState;AppManager.init(document.querySelector('#app')) .then(async ()=>{AppManager.add([form]);AppManager.open('simpleForm', {data:"Данные"}); })
Перед загрузкой элемента вызывается событие beforeLoad и вызывается метод onBeforeLoad. После загрузки элемента вызывается событие afterLoad и вызывается метод onAfterLoad.
В примере ниже обработаем события загрузки каждого элемента: IForm, IGroup, IElement, которые вложены друг в друга. Обратите внимание, что устанавливаем обработчики событий в методе onBeforeLoad формы, т.е. перед загрузкой формы.
Здесь в процессе загрузки формы simpleForm загружается группа simpleGroup, а в процессе загрузки группы загружается элемент simpleElement. Таким образом, в консоли видим следующую последовательность событий:
Описание класса события загрузки UILoadEvent смотрите здесь.
Объект класса UILoadEvent передается первым аргументом в методы onBeforeLoad и onAfterLoad, а также события beforeLoad и afterLoad. В примере покажем использование объекта класса UILoadEvent:
/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/import'./index.html';import'ui-organizer/page.css';import'ui-organizer/style.css';import'ui-organizer/vars.css';/*Описание формы simpleForm*/importtype {IForm} from"ui-organizer";import { AppManager, IElement, UILoadEvent} from"ui-organizer";exportvarform: IForm = <IForm>{type:'IForm',name:'simpleForm',elements: [ <IElement>{type:'IElement',name:'simpleElement',value:'Элемент',asynconBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){console.log(`Перед загрузкой элемента ${elem.name} данные: ${JSON.stringify(event.data)}`);event.data = {data:"Новые данные simpleElement"};//Данные будут переданы в метод onAfterLoad и событие afterLoad },asynconAfterLoad(event: UILoadEvent, form: IForm, elem: IElement){console.log(`После загрузки элемента ${elem.name} данные: ${JSON.stringify(event.data)}`);event.data = {data:"Повторно меняем данные simpleElement"};//Данные игнорируются далееevent.stop();//Игнорируется в методе onAfterLoad и событии afterLoad, т.к. элемент уже загружен. }, }, <IElement>{type:'IElement',name:'stopElement',value:'Элемент не загружен',asynconBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){console.log(`Перед загрузкой элемента ${elem.name} данные: ${JSON.stringify(event.data)}`);event.stop();//Остановили загрузку элемента },asynconAfterLoad(event: UILoadEvent, form: IForm, elem: IElement){//Не вызывается, т.к. загрузка прервана.console.log(`После загрузки элемента ${elem.name} данные: ${JSON.stringify(event.data)}`); }, }, ],}/*Инициализация приложения AppManager*/varGlobal: any = window;Global.AppManager = AppManager;Global.onpopstate = AppManager.onPopState;AppManager.init(document.querySelector('#app')) .then(async ()=>{AppManager.add([form]);AppManager.open('simpleForm', {data:"Данные"}); })
Видим, что использование объекта события загрузки UILoadEvent в методе onBeforeLoad (event.data - чтение и запись данных, и event.stop() - остановка загрузки) влияет на результат. А использование объекта события загрузки UILoadEvent в методе onAfterLoad целесообразно только для чтения данных, переданных в метод загрузки.
Обращаем внимание, что данные передаются в методы load только как справочная информация. Эти данные не фиксируются у элемента и не сохраняются. Данные, которые будут установлены для элемента доступны в методах onBeforeSetData и onAfterSetData.
Перед установкой данных элемента вызывается событие beforeSetData и вызывается метод onBeforeSetData. После установки данных элемента вызывается событие afterSetData и вызывается метод onAfterSetData.
В примере ниже обработаем события установки данных каждого элемента: IForm, IGroup, IDataElement, которые вложены друг в друга.
Здесь, в последней строке кода, устанавливаем данные при открытии формы. Мы не указали свойство bindingProperty ни у одного элемента, поэтому одни и те же данные устанавливаются в каждый вложенный элемент:
Обратите внимание, что в обработчике события beforeSetData элемента мы поменяли данные и элементу установлены эти измененные данные. Форме же и группе установлены первоначальные данные, которые были переданы в открытие формы.
Элементы с bindingProperty
Установим в каждый элемент свойство bindingProperty и установим соответствующие данные.
У группы и элемента видим данные undefined. Это произошло потому, что форма получила данные из свойства formData и дальше передала эти данные подэлементам. В этих данных, конечно же, не было свойства groupData и elementData.
Измените данные следующим образом и проверьте результат:
Описание класса события установки данных UISetDataEvent можно посмотреть здесь.
Объект класса UISetDataEvent передается первым аргументом в методы onBeforeSetData и onAfterSetData, а также события beforeSetData и afterSetData. В примере покажем использование объекта класса UISetDataEvent (мы расширили пример из раздела 2.2):
/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/import'./index.html';import'ui-organizer/page.css';import'ui-organizer/style.css';import'ui-organizer/vars.css';/*Описание формы simpleForm*/importtype {IDataElement, IForm} from"ui-organizer";import { AppManager, IElement, UILoadEvent, UISetDataEvent} from"ui-organizer";exportvarform: IForm = <IForm>{type:'IForm',name:'simpleForm',elements: [ <IDataElement>{type:'IDataElement',name:'simpleElement',value:'Элемент',asynconBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){console.log(`${elem.name} перед загрузкой данные: ${JSON.stringify(event.data)}`);event.data = {data:`Данные ${elem.name} из onBeforeLoad`};//Данные будут переданы в метод onAfterLoad и событие afterLoad },asynconAfterLoad(event: UILoadEvent, form: IForm, elem: IElement){console.log(`${elem.name} после загрузки данные: ${JSON.stringify(event.data)}`);event.data['some'] = {data:`Данные ${elem.name} из onAfterLoad`};//Данные игнорируются далееevent.stop();//Игнорируется в методе onAfterLoad и событии afterLoad, т.к. элемент уже загружен. },asynconBeforeSetData(event: UISetDataEvent, form: IForm, elem: IElement){console.log(`${elem.name} перед установкой данных: ${JSON.stringify(event.data)}`);event.data = {data:`Данные ${elem.name} из onBeforeSetData`};//Данные будут переданы в метод onAfterSetData и событие afterSetData },asynconAfterSetData(event: UISetDataEvent, form: IForm, elem: IElement){console.log(`${elem.name} после установки данных : ${JSON.stringify(event.data)}`);event.data['eva'] = {data:`Данные ${elem.name} из onAfterSetData`};//Данные игнорируются далее, т.к. уже установленыevent.stop();//Игнорируется в методе onAfterSetData и событии afterSetData, т.к. данные уже установлены. }, }, <IDataElement>{type:'IDataElement',name:'stopElement',value:'Элемент не загружен',asynconBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){console.log(`${elem.name} перед загрузкой данные: ${JSON.stringify(event.data)}`);event.stop();//Остановили загрузку элемента },asynconAfterLoad(event: UILoadEvent, form: IForm, elem: IElement){//Не вызывается, т.к. загрузка прервана.console.log(`${elem.name} после загрузки данные: ${JSON.stringify(event.data)}`); },asynconBeforeSetData(event: UISetDataEvent, form: IForm, elem: IElement){console.log(`${elem.name} перед установкой данных: ${JSON.stringify(event.data)}`);event.stop();//Остановили установку данных },asynconAfterSetData(event: UISetDataEvent, form: IForm, elem: IElement){//Не вызывается, т.к. установка данных прервана.console.log(`${elem.name} после установки данных : ${JSON.stringify(event.data)}`); }, }, ],}/*Инициализация приложения AppManager*/varGlobal: any = window;Global.AppManager = AppManager;Global.onpopstate = AppManager.onPopState;AppManager.init(document.querySelector('#app')) .then(async ()=>{AppManager.add([form]);AppManager.open('simpleForm', {data:"Данные"}); })
Видим, что использование объекта события установки данных UISetDataEvent в методе onBeforeSetData (event.data - чтение и запись данных, и event.stop() - остановка установки данных) влияет на результат. А использование объекта события установки данных UISetDataEvent в методе onAfterSetData целесообразно только для чтения данных, переданных в обработчик события.
Обращаем внимание, что данные устанавливаются для элемента stopElement, несмотря на то, что его загрузка прервана. Этот элемент присутствует на форме, только не загружен его HTMLElement.
Еще, обращаем внимание, что данные, которые мы изменили в методе onAfterLoad элемента simpleElement проигнорированы.
Все элементы управления вложены друг в друга. Поэтому при клике по элементу управления событие начала погружается потом всплывает. Пример, формы:
/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/import'./index.html';import'ui-organizer/page.css';import'ui-organizer/style.css';import'ui-organizer/vars.css';/*Описание формы simpleForm*/importtype {IForm, IGroup, UIMouseEvent} from"ui-organizer";import { AppManager, IElement, UILoadEvent} from"ui-organizer";exportvarform: IForm = <IForm>{type:'IForm',name:'simpleForm',elements: [ <IGroup>{type:'IGroup',name:'simpleGroup',elements: [ <IElement>{type:'IElement',name:'simpleElement',caption:'Элемент' }, ] }, ],onAfterLoad:asyncfunction (event: UILoadEvent, form: IForm, elem: IElement) {setClickListener(form, 'simpleElement');setClickListener(form, 'simpleGroup');setClickListener(form, 'simpleForm'); }}exportfunctionsetClickListener(_form: IForm, elemName: string) {var_elem: IElement = _form.getElement(elemName);_elem.on('click', (e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) => {console.log(`Клик по элементу ${elem.name} - погружение`); }, {capture:true});//{capture: true} или {} или undefined - это вызов обработчика при погружении_elem.on('click', (e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) => {console.log(`Клик по элементу ${elem.name} - всплытие`); }, {capture:false});//{capture: false} - это вызов обработчика при всплытии}/*Инициализация приложения AppManager*/varGlobal: any = window;Global.AppManager = AppManager;Global.onpopstate = AppManager.onPopState;AppManager.init(document.querySelector('#app')) .then(async ()=>{AppManager.add([form]);AppManager.open('simpleForm', {}); })
В примере элементы simpleForm, simpleGroup и simpleElement вложены друг в друга. После загрузки формы мы добавляем обработчики события click. Первый обработчик будет вызываться при погружении события от формы к элементу. Второй обработчик будет вызываться при всплытии события от элемента к форме. Параметр с единственным свойством capture, передаваемый в метод on() или addListener(), отвечает за то, на каком этапе будет вызываться обработчик: при погружении или всплытии.
После того, как вы кликните по элементу, в консоли увидите следующие сообщения:
Клик по элементу simpleForm - погружение
Клик по элементу simpleGroup - погружение
Клик по элементу simpleElement - погружение
Клик по элементу simpleElement - всплытие
Клик по элементу simpleGroup - всплытие
Клик по элементу simpleForm - всплытие
Для управления поведением используйте объект класса UIMouseEvent, который передается в обработчик события.
В обработчик события также передается form, elem и item. Где form - это форма, elem - элемент, по которому произведен клик, а item - подэлемент. Изменим обработчики следующим образом, чтобы увидеть передаваемые значения в обработчик:
exportfunctionsetClickListener(_form: IForm, elemName: string) {var_elem: IElement = _form.getElement(elemName);_elem.on('click', (e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) => {console.log(`Клик по элементу ${elem.name} (подэлемент ${item?.name}, непосредственно клик по ${e.targetElement.name}) - погружение`); }, {capture:true});//{capture: true} или {} или undefined - это вызов обработчика при погружении_elem.on('click', (e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) => {console.log(`Клик по элементу ${elem.name} (подэлемент ${item?.name}, непосредственно клик по ${e.targetElement.name}) - всплытие`); }, {capture:false});//{capture: false} - это вызов обработчика при всплытии}
После того, как вы кликните по элементу, в консоли увидите следующие сообщения:
Мы видим, что у формы подэлемент - группа, у группы подэлемент - элемент, у элемента подэлемент - undefined. Объект класса UIMouseEvent позволяет на каждом элементе управления узнать где непосредственно был произведен клик.
Пример ниже показывает последовательность вызова события click на разных элементах управления, а также значения аргументов, передаваемых обработчикам события click.
Сначала добавьте стили в html для красивой подсветки элементов, по которым сделан клик:
Содержание:
1.1. Cобытия элемента управления
1.2. Общая концепция 1.3. События, генерируемые пользователем 1.4. Добавление своих событий
2.1. События beforeLoad и afterLoad
2.2. Класс события загрузки UILoadEvent
3.1. События beforeSetData и afterSetData
3.2. Класс события установки данных UISetDataEvent
4.1. События click и dblclick
4.2. Пример формы демо (событие клик в действии)
4.3. Класс события мыши UIMouseEvent
Сразу посмотреть пример формы из раздела 4.2.
1. Общие сведения о событиях
1.1. События элемента управления
События реализованы как в модуле "event" node js и браузера. Библиотека ui-organizer здесь ничего нового не добавляет. Она только реализует возможность описать эти события.
Каждый элемент управления генерирует события. Например, объект (элемент управления), который реализует интерфейс
IElement
генерирует следующие события. Все остальные интерфейсы являются наследникамиIElement
, поэтому все элементы управления также генерируют эти события.Обработчики событий создаются:
onBeforeLoad
иonAfterLoad
, например:runtime
, например: Почему в интерфейсе IElement указаны методыonBeforeLoad
,onAfterLoad
и событияbeforeLoad
,afterLoad
?Потому что обработчики события могут быть разными и могут создаваться другими элементами, например:
В примере элемент
IElement
имеет обработчик событияonAfterLoad
со своей логикой. Форма добавляет к событиюafterLoad
элемента дополнительную логику.Загрузка элементов управления, как и другие события, генерируемые элементами управления (например, установка данных onBeforeSetData, onAfterSetData или редактирование onBeforeEdit, onAfterEdit), вызываются последовательно согласно вложенности элементов.
Т.е. сначала onBeforeLoad формы, потом onBeforeLoad вложенных в форму элементов, потом onBeforeLoad вложенных во вложенные элементы.
Затем, когда достигнем самого последнего вложенного элемента вызываются onAfterLoad этого элемента, потом onAfterLoad элемента, содержащего предыдущий, и так до onAfterLoad формы.
Более подробно о событии afterLoad и beforeLoad смотрите в разделе 2.
1.2. Общая концепция
События элементов управления можно разделить на три категории:
События, которые генерируются самими элементами управления, имеют в своем названии слова before и after. Т.е. вызываются до и после действия элемента как описано в разделе 1.1.. Такие события вызываются последовательно для каждого элемента в цепочке вложенных элементов управления.
Обработчикам событий этой категории первым аргументом передается объект события. С помощью объекта события вы получаете доступ к данным события, а также можете остановить событие
event.stop()
. Например, при загрузке элемента в обработчке события onBeforeLoad вы можете остановить загрузку конкретного элемента, тогда событие onAfterLoad этого элемента не будет вызвано.И еще, в отличие от второй категории, событие элемента не связано с таким же событием вложенных элементов.
События, которые генерируются в результате действий пользователя, не имеют вызовов до и после. Т.е. событие еже произошло и нам надо его обработать, как описано в разделе 1.3.. Такие события также вызываются последовательно по всей цепочке вложенных элементов, но с тем обстоятельством, что событие произошло на самом нижнем элементе (например, click) и события всех вышестоящих элементов связаны с этим фактом.
Обработчикам событий этой категории также первым аргументом передается объект события. С его помощью вы получаете доступ к данным события. а также можете остановить распространение событие
event.stop()
по аналогии с методом событияMouseEvent.stopPropagation()
браузера. Например, при click по элементу сначала click обрабатывает форма, затем вложенные элементы, затем искомый элемент по цепочке вложенности. На любом этапе вы можете остановить распространение этого события.Кроме остановки распространения события вы можете отключить предопределенные действия формы при возникновении таких событий
event.preventDefault()
(например, отключить навигацию по элементам управления клавишей Tab в событии keydown). Еще одной особенностью событий этой категории является то, что событие сначала опускается от формы до последнего элемента в цепочке вложенности, потом поднимается от элемента к форме. По аналогии с capturing и babbling при click в браузере.События, которые добавил разработчик, генерируются и обрабатываются по замыслу самого разработчика. Данные, передаваемые таким событиям, задаются также самим разработчиком, как описано в разделе1.4..
1.3. События, генерируемые пользователем
Пользователь может генерировать такие события как click, dblClick, keydown, keyup, focus, blur и др. Рассмотрим на примере объекта (элемент управления), который реализует интерфейс
IElement
событие click. Все остальные интерфейсы являются наследникамиIElement
, поэтому также предоставляют возможность обрабатывать это событие.Обработчики событий генерируемых пользователем вызываются последовательно по всей цепочке вложенных элементов:
В приведенном примере, если кликнуть по элементу, то вызовутся все обработчики по очереди, от формы до элемента. В результате получим следующий вывод в консоли:
Более подробно описание события click смотрите в разделе События мыши (click).
1.4. Добавление своих событий
Если вы хотите, чтобы ваш элемент управления имел дополнительное событие
someSetted
. Для этого задайте интерфейс. Конечно, вы можете не создавать интерфейс, это и так будет работать, но задание интерфейса убережет вас от ошибок и сэкономит кучу времени в будущем.Описание вашего элемента управления может быть следующим:
У формы в методе
onAfterLoad()
addListener()
(где добавляем обработчикelem.on('someSetted', ...)
)setSomeProperty()
, т.е. эмитируем событие (this.emit("someSetted",...)
).2. События загрузки (load)
2.1. События beforeLoad и afterLoad
Перед загрузкой элемента вызывается событие beforeLoad и вызывается метод onBeforeLoad.
После загрузки элемента вызывается событие afterLoad и вызывается метод onAfterLoad.
В примере ниже обработаем события загрузки каждого элемента: IForm, IGroup, IElement, которые вложены друг в друга.
Обратите внимание, что устанавливаем обработчики событий в методе onBeforeLoad формы, т.е. перед загрузкой формы.
Здесь в процессе загрузки формы simpleForm загружается группа simpleGroup, а в процессе загрузки группы загружается элемент simpleElement. Таким образом, в консоли видим следующую последовательность событий:
2.2. Класс события загрузки UILoadEvent
Описание класса события загрузки UILoadEvent смотрите здесь.
Объект класса UILoadEvent передается первым аргументом в методы onBeforeLoad и onAfterLoad, а также события beforeLoad и afterLoad.
В примере покажем использование объекта класса UILoadEvent:
Видим, что использование объекта события загрузки UILoadEvent в методе onBeforeLoad (event.data - чтение и запись данных, и event.stop() - остановка загрузки) влияет на результат. А использование объекта события загрузки UILoadEvent в методе onAfterLoad целесообразно только для чтения данных, переданных в метод загрузки.
Обращаем внимание, что данные передаются в методы load только как справочная информация. Эти данные не фиксируются у элемента и не сохраняются. Данные, которые будут установлены для элемента доступны в методах onBeforeSetData и onAfterSetData.
3. События установки данных (setData)
3.1. События beforeSetData и afterSetData
Перед установкой данных элемента вызывается событие beforeSetData и вызывается метод onBeforeSetData.
После установки данных элемента вызывается событие afterSetData и вызывается метод onAfterSetData.
В примере ниже обработаем события установки данных каждого элемента: IForm, IGroup, IDataElement, которые вложены друг в друга.
Здесь, в последней строке кода, устанавливаем данные при открытии формы. Мы не указали свойство bindingProperty ни у одного элемента, поэтому одни и те же данные устанавливаются в каждый вложенный элемент:
Обратите внимание, что в обработчике события beforeSetData элемента мы поменяли данные и элементу установлены эти измененные данные. Форме же и группе установлены первоначальные данные, которые были переданы в открытие формы.
Элементы с bindingProperty Установим в каждый элемент свойство bindingProperty и установим соответствующие данные.
Получаем следующий результат:
У группы и элемента видим данные undefined. Это произошло потому, что форма получила данные из свойства formData и дальше передала эти данные подэлементам. В этих данных, конечно же, не было свойства groupData и elementData.
Измените данные следующим образом и проверьте результат:
3.2. Класс события установки данных UISetDataEvent
Описание класса события установки данных UISetDataEvent можно посмотреть здесь.
Объект класса UISetDataEvent передается первым аргументом в методы onBeforeSetData и onAfterSetData, а также события beforeSetData и afterSetData.
В примере покажем использование объекта класса UISetDataEvent (мы расширили пример из раздела 2.2):
Видим, что использование объекта события установки данных UISetDataEvent в методе onBeforeSetData (event.data - чтение и запись данных, и event.stop() - остановка установки данных) влияет на результат. А использование объекта события установки данных UISetDataEvent в методе onAfterSetData целесообразно только для чтения данных, переданных в обработчик события.
Обращаем внимание, что данные устанавливаются для элемента stopElement, несмотря на то, что его загрузка прервана. Этот элемент присутствует на форме, только не загружен его HTMLElement.
Еще, обращаем внимание, что данные, которые мы изменили в методе onAfterLoad элемента simpleElement проигнорированы.
4. События мыши (click)
4.1. События click и dblclick
Все элементы управления вложены друг в друга. Поэтому при клике по элементу управления событие начала погружается потом всплывает.
Пример, формы:
В примере элементы
simpleForm
,simpleGroup
иsimpleElement
вложены друг в друга. После загрузки формы мы добавляем обработчики события click. Первый обработчик будет вызываться при погружении события от формы к элементу. Второй обработчик будет вызываться при всплытии события от элемента к форме. Параметр с единственным свойствомcapture
, передаваемый в метод on() или addListener(), отвечает за то, на каком этапе будет вызываться обработчик: при погружении или всплытии.После того, как вы кликните по элементу, в консоли увидите следующие сообщения:
Для управления поведением используйте объект класса UIMouseEvent, который передается в обработчик события.
В обработчик события также передается form, elem и item. Где form - это форма, elem - элемент, по которому произведен клик, а item - подэлемент. Изменим обработчики следующим образом, чтобы увидеть передаваемые значения в обработчик:
После того, как вы кликните по элементу, в консоли увидите следующие сообщения:
Мы видим, что у формы подэлемент - группа, у группы подэлемент - элемент, у элемента подэлемент - undefined. Объект класса UIMouseEvent позволяет на каждом элементе управления узнать где непосредственно был произведен клик.
4.2. Пример формы демо (событие клик в действии)
Пример ниже показывает последовательность вызова события click на разных элементах управления, а также значения аргументов, передаваемых обработчикам события click.
Сначала добавьте стили в html для красивой подсветки элементов, по которым сделан клик:
Потом опишите форму:
Generated using TypeDoc