Options
All
  • Public
  • Public/Protected
  • All
Menu

Namespace PageEvents

Содержание:

  1. Общие сведения
    1.1. Cобытия элемента управления
    1.2. Общая концепция 1.3. События, генерируемые пользователем 1.4. Добавление своих событий
  2. События загрузки (load)
    2.1. События beforeLoad и afterLoad
    2.2. Класс события загрузки UILoadEvent
  3. События установки данных (setData)
    3.1. События beforeSetData и afterSetData
    3.2. Класс события установки данных UISetDataEvent
  4. События мыши (click)
    4.1. События click и dblclick
    4.2. Пример формы демо (событие клик в действии)
    4.3. Класс события мыши UIMouseEvent

Сразу посмотреть пример формы из раздела 4.2.

1. Общие сведения о событиях

1.1. События элемента управления

События реализованы как в модуле "event" node js и браузера. Библиотека ui-organizer здесь ничего нового не добавляет. Она только реализует возможность описать эти события.

Каждый элемент управления генерирует события. Например, объект (элемент управления), который реализует интерфейс IElement генерирует следующие события. Все остальные интерфейсы являются наследниками IElement, поэтому все элементы управления также генерируют эти события.

export interface IElement<T=IElementEvents> extends StrictEventEmitter<EventEmitter, T>{
    onBeforeLoad?(event: UILoadEvent, form: IForm, elem: IElement): Promise<void>;
    onAfterLoad?(event: UILoadEvent, form: IForm, elem: IElement): Promise<void>;
    /**другие методы обработчики событий */
}
export interface IElementEvents {
    beforeLoad(event: UILoadEvent, form: IForm, elem: IElement): void,
    afterLoad(event: UILoadEvent, form: IForm, elem: IElement): void, 
    /**другие события */
}

Обработчики событий создаются:

  • при описании объекта в методах onBeforeLoad и onAfterLoad, например:
    export var baseElem: IElement = <IElement>{
      type: 'IElement',
      onAfterLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
          elem.addClass('loading');
      },
      onAfterLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
          elem.removeClass('loading');
      }
    }
    
  • или в runtime, например:
    function addListeners(elem: IElement) {
      elem.on("beforeLoad", (event: UILoadEvent, form: IForm, elem: IElement) => {
          console.log(`Элемент ${elem.name} перед загрузкой.`);
      })
      elem.on("afterLoad", (event: UILoadEvent, form: IForm, elem: IElement) => {
          console.log(`Элемент ${elem.name} загружен.`);
      })
    }
    
    Почему в интерфейсе 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*/
import type {IForm, IElement} from "ui-organizer";
import { AppManager, Grouping, Flex, UILoadEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    flex: Flex.flexible,
    grouping: Grouping.vertical,
    elements: [
        <IElement>{
            type: 'IElement',
            name: 'header',
            //Обработчик события afterLoad элемента header
            onAfterLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
                elem.value = 'Мое приложение';
            }
        },
    ],
    onBeforeLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
        var header: IElement = form.getElement('header');
        //Второй обработчик события afterLoad элемента header
        header.on("afterLoad", (_event, _form, _elem) => {
            _elem.addClass('header');
        })
    }
}

/*Инициализация приложения AppManager*/
var Global: 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.

1.2. Общая концепция

События элементов управления можно разделить на три категории:

  1. События, которые генерируются самими элементами управления(onBeforeLoad, onAfterLoad; onBeforeSetData, onAfterSetData; onBeforeEdit, onAfterEdit и др.).
  2. События, которые генерируются в результате действий пользователя (click, dblClick; keydown, keyup; focus, blur и др).
  3. События, которые добавил разработчик.

События, которые генерируются самими элементами управления, имеют в своем названии слова 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, поэтому также предоставляют возможность обрабатывать это событие.

export interface IElement<T=IElementEvents> extends StrictEventEmitter<EventEmitter, T>{
    onClick?(event: UIMouseEvent, form: IForm, elem: IElement, item: IElement): Promise<void>;
    /**другие методы обработчики событий */
}
export interface IElementEvents {
    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*/
import type {IForm, IElement, IGroup} from "ui-organizer";
import { AppManager, UIMouseEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    elements: [
        <IGroup>{
            type: 'IGroup',
            elements: [
                <IElement>{
                    type: 'IElement',
                    name: 'simpleElement',
                    caption: 'Элемент',
                    async onClick(e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) {
                        console.log(`Элемент. Клик по элементу ${elem.name} - погружение`);
                    }
                },
            ],
            async onClick(e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) {
                console.log(`Группа знает, что кликнули по элементу ${e.targetElement.name} - погружение`);
            }
        },
    ],
    async onClick(e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) {
        console.log(`Форма знает, что кликнули по элементу ${e.targetElement.name} - погружение`);
    }
}

/*Инициализация приложения AppManager*/
var Global: any = window;
Global.AppManager = AppManager;
Global.onpopstate = AppManager.onPopState;

AppManager.init(document.querySelector('#app'))
    .then(async ()=>{
        AppManager.add([form]);
        AppManager.open('simpleForm', {data: "Данные"});
    })

В приведенном примере, если кликнуть по элементу, то вызовутся все обработчики по очереди, от формы до элемента. В результате получим следующий вывод в консоли:

Форма знает, что кликнули по элементу simpleElement - погружение
Группа знает, что кликнули по элементу simpleElement - погружение
Элемент. Клик по элементу simpleElement - погружение

Более подробно описание события click смотрите в разделе События мыши (click).

1.4. Добавление своих событий

Если вы хотите, чтобы ваш элемент управления имел дополнительное событие someSetted. Для этого задайте интерфейс. Конечно, вы можете не создавать интерфейс, это и так будет работать, но задание интерфейса убережет вас от ошибок и сэкономит кучу времени в будущем.

Описание вашего элемента управления может быть следующим:

/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';

/*Описание формы simpleForm*/
import type {IForm, IElement, IElementEvents} from "ui-organizer";
import { AppManager, UIForm, UILoadEvent } from "ui-organizer";

//Описание пользовательского интерфейса наследника IElement
export interface ISomeElement<T=ISomeElementEvents> extends IElement<T>  {
    someProperty: string;
    setSomeProperty(val: string): void;
}

export interface ISomeElementEvents extends IElementEvents{
    someSetted(form: IForm, element: IElement): void,
}

//Описание элемента elem и формы form.
export var elem: ISomeElement = <ISomeElement>{
    type: 'IElement',
    name: 'someElement',
    someProperty: undefined,
    setSomeProperty: function (val: string) {
        this.someProperty = val;
        this.emit("someSetted", this.form, this);
    }
}

export let form: IForm = <IForm>{
    type: UIForm,
    name: 'simpleForm',
    elements:[
        elem
    ],
    async onAfterLoad(event: UILoadEvent, form: IForm, elem:IElement){
        let someElem: ISomeElement = form.getElement('someElement') as ISomeElement;
        someElem.value = 'someElement';
        addListener(someElem);
        someElem.setSomeProperty('OK');
    }
}

export function addListener(elem:ISomeElement){
    elem.on('someSetted', async (form:IForm, elem:ISomeElement) => {
        console.log(`${elem.name}: someProperty установлен в ${elem.someProperty}.`);
    })
}

/*Инициализация приложения AppManager*/
var Global: any = window;
Global.AppManager = AppManager;
Global.onpopstate = AppManager.onPopState;

AppManager.init(document.querySelector('#app'))
    .then(async ()=>{
        AppManager.add([form]);
        AppManager.open('simpleForm', {data: "Данные"});
    })

У формы в методе onAfterLoad()

  • вызываем addListener() (где добавляем обработчик elem.on('someSetted', ...))
  • и вызываем setSomeProperty(), т.е. эмитируем событие (this.emit("someSetted",...)).

2. События загрузки (load)

2.1. События beforeLoad и afterLoad

Перед загрузкой элемента вызывается событие beforeLoad и вызывается метод onBeforeLoad.
После загрузки элемента вызывается событие afterLoad и вызывается метод onAfterLoad.

В примере ниже обработаем события загрузки каждого элемента: IForm, IGroup, IElement, которые вложены друг в друга.
Обратите внимание, что устанавливаем обработчики событий в методе onBeforeLoad формы, т.е. перед загрузкой формы.

/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';

/*Описание формы simpleForm*/
import type {IForm, IGroup} from "ui-organizer";
import { AppManager, IElement, UILoadEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    elements: [
        <IGroup>{
            type: 'IGroup',
            name: 'simpleGroup',
            elements: [
                <IElement>{
                    type: 'IElement',
                    name: 'simpleElement',
                    value: 'Элемент',
                },
            ]
        },
    ],
    onBeforeLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
        console.log(`Элемент ${elem.name} - событие beforeLoad`);
        setLoadListener(form, 'simpleElement');
        setLoadListener(form, 'simpleGroup');
        setLoadListener(form, 'simpleForm');
    }
}

export function setLoadListener(_form: IForm, elemName: string) {
    var _elem: IElement = _form.getElement(elemName);

    _elem.on('beforeLoad', (e: UILoadEvent, form: IForm, elem: IElement) => {
        console.log(`Элемент ${elem.name} - событие beforeLoad`);
    });

    _elem.on('afterLoad', (e: UILoadEvent, form: IForm, elem: IElement) => {
        console.log(`Элемент ${elem.name} - событие afterLoad`);
    });
}

/*Инициализация приложения AppManager*/
var Global: 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. Таким образом, в консоли видим следующую последовательность событий:

Элемент simpleForm - событие beforeLoad
Элемент simpleGroup - событие beforeLoad
Элемент simpleElement - событие beforeLoad
Элемент simpleElement - событие afterLoad
Элемент simpleGroup - событие afterLoad
Элемент simpleForm - событие afterLoad

2.2. Класс события загрузки UILoadEvent

Описание класса события загрузки 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*/
import type {IForm} from "ui-organizer";
import { AppManager, IElement, UILoadEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    elements: [
        <IElement>{
            type: 'IElement',
            name: 'simpleElement',
            value: 'Элемент',
            async onBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){
                console.log(`Перед загрузкой элемента ${elem.name} данные: ${JSON.stringify(event.data)}`);
                event.data = {data: "Новые данные simpleElement"};//Данные будут переданы в метод onAfterLoad и событие afterLoad
            },
            async onAfterLoad(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: 'Элемент не загружен',
            async onBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){
                console.log(`Перед загрузкой элемента ${elem.name} данные: ${JSON.stringify(event.data)}`);
                event.stop();//Остановили загрузку элемента
            },
            async onAfterLoad(event: UILoadEvent, form: IForm, elem: IElement){//Не вызывается, т.к. загрузка прервана.
                console.log(`После загрузки элемента ${elem.name} данные: ${JSON.stringify(event.data)}`);
            },
        },                
    ],
}

/*Инициализация приложения AppManager*/
var Global: 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.

3. События установки данных (setData)

3.1. События beforeSetData и afterSetData

Перед установкой данных элемента вызывается событие beforeSetData и вызывается метод onBeforeSetData.
После установки данных элемента вызывается событие afterSetData и вызывается метод onAfterSetData.

В примере ниже обработаем события установки данных каждого элемента: IForm, IGroup, IDataElement, которые вложены друг в друга.

/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';

/*Описание формы simpleForm*/
import type {IDataElement, IForm, IGroup} from "ui-organizer";
import { AppManager, IElement, UILoadEvent, UISetDataEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    elements: [
        <IGroup>{
            type: 'IGroup',
            name: 'simpleGroup',
            elements: [
                <IDataElement>{
                    type: 'IDataElement',
                    name: 'simpleElement',
                    value: 'Элемент с данными',
                },
            ]
        },
    ],
    onBeforeLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
        setDataListeners(form, 'simpleElement');
        setDataListeners(form, 'simpleGroup');
        setDataListeners(form, 'simpleForm');
    }
}

export function setDataListeners(_form: IForm, elemName: string) {
    var _elem: IDataElement = _form.getElement(elemName) as IDataElement;

    _elem.on('beforeSetData', (e: UISetDataEvent, form: IForm, elem: IDataElement) => {
        console.log(`Элемент ${elem.name} - событие beforeSetData, данные:${JSON.stringify(e.data)}`);
        if(elem.name == 'simpleElement') e.data = {elementData: 'Данные элемента'}
    });

    _elem.on('afterSetData', (e: UISetDataEvent, form: IForm, elem: IDataElement) => {
        console.log(`Элемент ${elem.name} - событие afterSetData, данные:${JSON.stringify(e.data)}`);
    });
}

/*Инициализация приложения AppManager*/
var Global: any = window;
Global.AppManager = AppManager;
Global.onpopstate = AppManager.onPopState;

AppManager.init(document.querySelector('#app'))
    .then(async ()=>{
        AppManager.add([form]);
        AppManager.open('simpleForm', {formData: 'Данные формы'});
    })

Здесь, в последней строке кода, устанавливаем данные при открытии формы. Мы не указали свойство bindingProperty ни у одного элемента, поэтому одни и те же данные устанавливаются в каждый вложенный элемент:

Элемент simpleForm - событие beforeSetData, данные:{"formData":"Данные формы"}
Элемент simpleGroup - событие beforeSetData, данные:{"formData":"Данные формы"}
Элемент simpleElement - событие beforeSetData, данные:{"formData":"Данные формы"}
Элемент simpleElement - событие afterSetData, данные:{"elementData":"Данные элемента"}
Элемент simpleGroup - событие afterSetData, данные:{"formData":"Данные формы"}
Элемент simpleForm - событие afterSetData, данные:{"formData":"Данные формы"}

Обратите внимание, что в обработчике события beforeSetData элемента мы поменяли данные и элементу установлены эти измененные данные. Форме же и группе установлены первоначальные данные, которые были переданы в открытие формы.

Элементы с bindingProperty Установим в каждый элемент свойство bindingProperty и установим соответствующие данные.

/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';

/*Описание формы simpleForm*/
import type {IDataElement, IForm, IGroup} from "ui-organizer";
import { AppManager, IElement, UILoadEvent, UISetDataEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    bindingProperty: 'formData',
    elements: [
        <IGroup>{
            type: 'IGroup',
            name: 'simpleGroup',
            bindingProperty: 'groupData',
            elements: [
                <IDataElement>{
                    type: 'IDataElement',
                    name: 'simpleElement',
                    bindingProperty: 'elementData',
                    value: 'Элемент с данными',
                },
            ]
        },
    ],
    onBeforeLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
        setDataListeners(form, 'simpleElement');
        setDataListeners(form, 'simpleGroup');
        setDataListeners(form, 'simpleForm');
    }
}

export function setDataListeners(_form: IForm, elemName: string) {
    var _elem: IDataElement = _form.getElement(elemName) as IDataElement;

    _elem.on('beforeSetData', (e: UISetDataEvent, form: IForm, elem: IDataElement) => {
        console.log(`Элемент ${elem.name} - событие beforeSetData, данные:${JSON.stringify(e.data)}`);
    });

    _elem.on('afterSetData', (e: UISetDataEvent, form: IForm, elem: IDataElement) => {
        console.log(`Элемент ${elem.name} - событие afterSetData, данные:${JSON.stringify(e.data)}`);
    });
}

/*Инициализация приложения AppManager*/
var Global: any = window;
Global.AppManager = AppManager;
Global.onpopstate = AppManager.onPopState;

AppManager.init(document.querySelector('#app'))
    .then(async ()=>{
        AppManager.add([form]);
        AppManager.open('simpleForm', {
            formData: 'Данные формы',
            groupData: 'Данные группы',
            elementData: 'Данные группы'
        });
    })

Получаем следующий результат:

Элемент simpleForm - событие beforeSetData, данные:"Данные формы"
Элемент simpleGroup - событие beforeSetData, данные:undefined
Элемент simpleElement - событие beforeSetData, данные:undefined
Элемент simpleElement - событие afterSetData, данные:undefined
Элемент simpleGroup - событие afterSetData, данные:undefined
Элемент simpleForm - событие afterSetData, данные:"Данные формы"

У группы и элемента видим данные undefined. Это произошло потому, что форма получила данные из свойства formData и дальше передала эти данные подэлементам. В этих данных, конечно же, не было свойства groupData и elementData.

Измените данные следующим образом и проверьте результат:

AppManager.open('simpleForm', {
    formData: {
        groupData: {
            elementData: 'Данные'
        }
    }    
});

3.2. Класс события установки данных UISetDataEvent

Описание класса события установки данных 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*/
import type {IDataElement, IForm} from "ui-organizer";
import { AppManager, IElement, UILoadEvent, UISetDataEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    elements: [
        <IDataElement>{
            type: 'IDataElement',
            name: 'simpleElement',
            value: 'Элемент',
            async onBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){
                console.log(`${elem.name} перед загрузкой данные: ${JSON.stringify(event.data)}`);
                event.data = {data: `Данные ${elem.name} из onBeforeLoad`};//Данные будут переданы в метод onAfterLoad и событие afterLoad
            },
            async onAfterLoad(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, т.к. элемент уже загружен.
            },
            async onBeforeSetData(event: UISetDataEvent, form: IForm, elem: IElement){
                console.log(`${elem.name} перед установкой данных: ${JSON.stringify(event.data)}`);
                event.data = {data: `Данные ${elem.name} из onBeforeSetData`};//Данные будут переданы в метод onAfterSetData и событие afterSetData
            },
            async onAfterSetData(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: 'Элемент не загружен',
            async onBeforeLoad(event: UILoadEvent, form: IForm, elem: IElement){
                console.log(`${elem.name} перед загрузкой данные: ${JSON.stringify(event.data)}`);
                event.stop();//Остановили загрузку элемента
            },
            async onAfterLoad(event: UILoadEvent, form: IForm, elem: IElement){//Не вызывается, т.к. загрузка прервана.
                console.log(`${elem.name} после загрузки данные: ${JSON.stringify(event.data)}`);
            },
            async onBeforeSetData(event: UISetDataEvent, form: IForm, elem: IElement){
                console.log(`${elem.name} перед установкой данных: ${JSON.stringify(event.data)}`);
                event.stop();//Остановили установку данных
            },
            async onAfterSetData(event: UISetDataEvent, form: IForm, elem: IElement){//Не вызывается, т.к. установка данных прервана.
                console.log(`${elem.name} после установки данных : ${JSON.stringify(event.data)}`);
            },       
        },                
    ],
}

/*Инициализация приложения AppManager*/
var Global: 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 проигнорированы.

4. События мыши (click)

4.1. События click и dblclick

Все элементы управления вложены друг в друга. Поэтому при клике по элементу управления событие начала погружается потом всплывает.
Пример, формы:

/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';

/*Описание формы simpleForm*/
import type {IForm, IGroup, UIMouseEvent} from "ui-organizer";
import { AppManager, IElement, UILoadEvent} from "ui-organizer";

export var form: IForm = <IForm>{
    type: 'IForm',
    name: 'simpleForm',
    elements: [
        <IGroup>{
            type: 'IGroup',
            name: 'simpleGroup',
            elements: [
                <IElement>{
                    type: 'IElement',
                    name: 'simpleElement',
                    caption: 'Элемент'
                },
            ]
        },
    ],
    onAfterLoad: async function (event: UILoadEvent, form: IForm, elem: IElement) {
        setClickListener(form, 'simpleElement');
        setClickListener(form, 'simpleGroup');
        setClickListener(form, 'simpleForm');
    }
}

export function setClickListener(_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*/
var Global: 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 - подэлемент. Изменим обработчики следующим образом, чтобы увидеть передаваемые значения в обработчик:

export function setClickListener(_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} - это вызов обработчика при всплытии
}

После того, как вы кликните по элементу, в консоли увидите следующие сообщения:

Клик по элементу simpleForm (подэлемент simpleGroup, непосредственно клик по simpleElement) - погружение
Клик по элементу simpleGroup (подэлемент simpleElement, непосредственно клик по simpleElement) - погружение
Клик по элементу simpleElement (подэлемент undefined, непосредственно клик по simpleElement) - погружение
Клик по элементу simpleElement (подэлемент undefined, непосредственно клик по simpleElement) - всплытие
Клик по элементу simpleGroup (подэлемент simpleElement, непосредственно клик по simpleElement) - всплытие
Клик по элементу simpleForm (подэлемент simpleGroup, непосредственно клик по simpleElement) - всплытие

Мы видим, что у формы подэлемент - группа, у группы подэлемент - элемент, у элемента подэлемент - undefined. Объект класса UIMouseEvent позволяет на каждом элементе управления узнать где непосредственно был произведен клик.

4.2. Пример формы демо (событие клик в действии)

Пример ниже показывает последовательность вызова события click на разных элементах управления, а также значения аргументов, передаваемых обработчикам события click.

Сначала добавьте стили в html для красивой подсветки элементов, по которым сделан клик:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>ui-organizer simple form</title>
    <link rel="stylesheet" href="page.css">
    <link rel="stylesheet" href="style.css">
    <style>
        /* Просто, поместили форму посередине страницы */
        body { width: 80%; margin: 0px auto 0px auto}
        .clicked {background-color: green}
        .clickedtransition {transition: background-color 2s linear}
    </style>
</head>
<body>
    <div id="app">
    </div>
    <script type="text/javascript" src="index.js"></script>
</body>
</html>

Потом опишите форму:

/*Инструкции для WebPack, который скопирует эти файлы в ваш каталог*/
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';

/*Описание формы simpleForm*/
import type {UIMouseEvent, IDataElement, IElement, IForm, IList, IListItem, IMenu, IMenuItem, IGroup, IStr, IProperty} from "ui-organizer";
import { AppManager, UIList, UIForm, UIGroup, UIMenu, UIMenuItem, UISetDataEvent, UILoadEvent, UIItemEvent} from "ui-organizer";
import { ContentType, Flex, Grouping, JustifyContent } from "ui-organizer";


interface IMyForm extends IForm{
    eventList?: IList,
    dataList?: IList
}

/**Названия элементов управления вынесены в константы, так как используются в разных местах.*/
const menuName = 'menu';
const menuItemName = 'menuItem'
const subMenuName = 'subMenu';
const subMenuItemName = 'subMenuItem'
const element1Name = 'element1';
const element2Name = 'element2';
const groupName = 'group';
const propertyName = 'property';
const strName = 'str';
const listName = 'testList';
const formName = 'simpleForm';
const eventListName = 'eventList';
const dataListName = 'dataList';


/**Описание меню вынесено отдельно для примера и чтобы не загромождать описание формы*/
const menuElement = <IMenu>{
    type: UIMenu,
    name: menuName,
    caption: 'Меню',
    justifyContent: JustifyContent.spaceBetween,
    elements: [
        <IMenuItem>{
            type: UIMenuItem,
            name: menuItemName,
            caption: 'Кнопка меню',
            menu: <IMenu>{
                type: UIMenu,
                name: subMenuName,
                caption: 'Подменю',
                elements: [
                    <IMenuItem>{
                        type: UIMenuItem,
                        name: subMenuItemName,
                        caption: 'Кнопка подменю',
                    }
                ]
            }
        }
    ]
}

/**Описание формы включает описание нескольких элементов управления*/
export let form: IMyForm = <IMyForm>{
    type: UIForm,
    name: formName,
    caption: 'Форма',
    elements: [
        menuElement,
        /**
         * ГРУППА
         */
        <IGroup>{
            type: UIGroup,
            caption: 'Группа',
            name: groupName,
            bordered: true,
            flex: Flex.fixed,
            elements: [
                /**
                 * ЭЛЕМЕНТ ВНУТРИ ГРУППЫ
                 */                
                <IElement>{
                    type: 'IElement',
                    name: element1Name,
                    caption: 'Элемент один',
                    bordered: true
                },                
            ]
        },
        /**
         * ЭЛЕМЕНТ
         */
        <IElement>{
            type: 'IElement',
            name: element2Name,
            caption: 'Элемент два',
            bordered: true
        },
        /**
         * ПОЛЕ ВВОДА
         */        
        <IProperty>{
            type: 'IProperty',
            name: propertyName,
            caption: 'Поле ввода',
            bordered: true
        },
        /**
         * СПИСОК
         */           
        <IList>{
            type: UIList,
            name: listName,
            caption: 'Список',
            flex: Flex.fixed,
            value: ["Строка списка"],
            async onAfterAddItem(event: UIItemEvent, form: IForm, elem: IList) {
                let item = event.item;            
                item.element.caption = event.value;
                item.element.name = 'listItem';
                setClickListener(form, item.element.name);
            },
        },
        /**
         * ПОЯСНЕНИЕ
         */           
        <IStr>{
            type: 'IStr',
            name: strName,
            caption: 'Пояснение',
            contentType: ContentType.md,
            flex: Flex.fixed,
            async onBeforeSetData(event: UISetDataEvent) {
                let str: string = `___
В **Списке событий** отображается последовательность событий при клике (сначала погружаемся (capturing) от формы к элементу, потом всплываем (babbling) от элемента к форме). 
В списке **Аргументы обработчика события** отображаются значения аргументов, передаваемых в обработчик события click каждого элемента (см. пример).

\`\`\`typescript
function setClickListener(elem:IElement, capturing: boolean){//true - capturing, false - bubbling
    elem.on('click', (e: UIMouseEvent, form: IForm, elem: IElement, item: IElement) => {
        console.log(elem.name);
    }, {capture: capturing});
} 
\`\`\`
___`;
                event.data = str;
            },
        },
        /**
         * ИНФОРМАЦИОННАЯ (ДВА СПИСКА) ГРУППА БЕЗ ОБРАБОЧИКОВ СОБЫТИЯ
         */         
        <IGroup>{
            type: UIGroup,
            name: 'listGroup',
            grouping: Grouping.horizontal,
            flex: Flex.fixed,
            elements: [
                <IList>{
                    type: UIList,
                    name: eventListName,
                    caption: 'Список событий:',
                    // size: '50%',
                    bordered: true,                   
                    async onAfterLoad(event: UILoadEvent, form: IMyForm) {
                        this.dataList = form.getElement('dataList') as IList;
                    },
                    async onClick(e:UIMouseEvent, form: IMyForm, elem: IList, item: IDataElement):Promise<void>{
                        this.dataList?.setData(item.getData().eventData);
                    }
                },
                <IList>{
                    type: UIList,
                    name: dataListName,
                    caption: 'Аргументы обработчика события:',                    
                    bordered: true,
                }
            ]
        }
    ],
    /**Продолжение описания формы - установка обработчиков click и подготовка списков с информацией о событии */
    async onAfterLoad(event: UILoadEvent, form: IMyForm, elem) {
        form.eventList = form.getElement(eventListName) as IList;
        form.dataList = form.getElement(dataListName) as IList;

        setClickListener(form, menuName);
        setClickListener(form, menuItemName);
        setClickListener(form, subMenuName);
        setClickListener(form, subMenuItemName);
        setClickListener(form, element1Name);
        setClickListener(form, element2Name);
        setClickListener(form, groupName);
        setClickListener(form, propertyName);
        setClickListener(form, listName);
        setClickListener(form, strName);
        setClickListener(form, formName);

        this.on('click', (e: UIMouseEvent, form: IMyForm, elem: IElement, item: IElement) => {
            if (item?.name != 'listGroup') {
                form.eventList?.clear();
                form.dataList?.clear();
            }
        });
    },
    data: {
    }
}

/**Функция устанавливает обработчики события click для погружения и всплытия */
export function setClickListener(_form: IMyForm, elemName: string) {
    var _elem: IDataElement = _form.getElement(elemName) as IDataElement;
    (<IElement>_elem).on('click', (e: UIMouseEvent, form: IMyForm, elem: IElement, item: IElement) => {
        if (item?.name != 'listGroup') {
            pushToList(`${_elem.caption}: clickCapturing`, e, form, elem, item);
        }
    });//Погружение
    (<IElement>_elem).on('click', (e: UIMouseEvent, form: IMyForm, elem: IElement, item: IElement) => {
        if (item?.name != 'listGroup') {
            pushToList(`${_elem.caption}: clickBubbling`, e, form, elem, item);
        }
    }, {capture: false});//Всплытие
}

/**Функция заполняет список с информацией о событии click и вызывает анимацию элемента, по которому кликнули */
async function pushToList(eventName: string, e: UIMouseEvent, form: IMyForm, elem: IElement, item: IElement): Promise<void> {
    let listItem: IListItem = await form.eventList?.addItem(eventName);
    listItem.setData({
        eventName: eventName,
        eventData: [
            `e.targetElement: ${e.targetElement.caption}`,
            `form: ${form.caption}`,
            `elem: ${elem.caption}`,
            `item: ${item?.caption}`
        ]
    });
    if (e.targetElement == elem) {
        elem.addClass('clicked');
        setTimeout(() => {
            elem.addClass('clickedtransition');
            elem.removeClass('clicked');
            setTimeout(() => { 
                elem.removeClass('clickedtransition');
            },2000)
        }, 30);
    }
}


/*Инициализация приложения AppManager*/
var Global: any = window;
Global.AppManager = AppManager;
Global.onpopstate = AppManager.onPopState;

AppManager.init(document.querySelector('#app'))
    .then(async ()=>{
        AppManager.add([form]);
        AppManager.open('simpleForm', {});
    })

Generated using TypeDoc