Сразу прошу прощения за не самую лёгкую подачу информации - мне всегда сложно давалось написание текста. Просто хотелось поделиться своими идеями.
Я думаю, все со мной согласятся, что для решения почти любой бизнес-задачи возможностей экосистемы 1С Предприятие более чем достаточно. Однако, изредка, могут появиться особо требовательные к интерфейсу заказчики, которым кровь из носу надо кликать по карте и автоматически заполнять данные адреса в карточке контрагента и тд. К сожалению для реализации таких требований приходится плясать с бубном вокруг нового типа "ВнешнийОбъект". Как потом отлаживать и поддерживать решения, написанные наполовину на javascrу я вообще молчу . В результате таких плясок и родилась идея написания подсистемы, которая позволит реализовать бОльшую часть логики на 1С, где будет легко её отлаживать, расширять и поддерживать. В статье изложу основную идею, и приложу первую версию моей подсистемы. Конечно там ещё много чего надо сделать и имеется куча идей куда можно развиваться. Итак, приступим.
Для начала необходимо реализовать возможность вызова функции eval из контекста 1C, так как она в ПолеHTML изначально не работает. При этом eval доступна при вызове из javascript. Это открывает перед нами возможность перехитрить платформу и переопределить вызов функции.
Для добавления скриптов к документу ХТМЛ я использую событие формы "ПриСозданииНаСервере". Добавим к нашему ХТМЛ документу тег SCRIPT в котором переопредели вызов функции eval так, чтобы он стал доступен из 1С.
Перед подключение дополнительных скриптов к html документу надо учесть следующее ограничение:
- Реквизит поля ХТМЛ должен содержать текст HTML документа, а не адрес расположения страницы (чтобы мы могли программно изменить DOM модель документа и подключить дополнительные файлы скриптов). Идея с добавлением тега script с помощью вызова функций addChild у меня почему-то не сработала. Видимо WebKit не выполняет скрипты, если они были добавлены после загрузки страницы. А без возможность вызова функции eval мы не можем добавлять свои функции в контекст js динамически. Получается замкнутый круг.
Теперь нам доступно выполнение произвольного кода javascript из 1С. Для примера вычисление суммы с помощью функции eval:
Вызов javascript из 1С мы реализовали. Теперь надо реализовать вызов кода 1С из javascript. Данные из JS можно передать в 1С, если перехватывать событие элемента формы "ПриНажатии". Для этого добавим функцию вызова 1С из контекста JS.
Таким образом мы заложили основу. Имея возможность выполнять произвольный код JS и перехватывать события - мы можем реализовать любую логику в коде 1С. Несомненным плюсом является нормальная отладка решения в контексте 1С, без необходимости использовать отладчик WebKit (который частенько роняет платформу). Однако для реализации сложной логики придётся добавить кучу новых событий и их обработчиков, в итоге получится не самый простой и читаемый код.
Я же хотел сделать универсальный программный интерфейс в 1С, который позволит практически без использования кода на JS создавать решения с HTML интерфейсом.
Моя основная идея заключается в использовании системы событий javascript (addEventListener), в связке с использованием объектов 1С "ОписаниеОповещения". Для этого необходимо выполнить следующую последовательность действий:
- создать описание оповещения с присвоением ему уникального идентификатора.
- "Закешировать" в клиентской переменной идентификатор и обработчик оповещения (в моей подсистеме я использую глобальную клиентскую переменную, но можно использовать и переменную на форме).
- создать обработчик события на элементе DOM модели документа, и присвоить ему обработчик, который будет вызывать функцию sendEvent и передавать в данные события идентификатор конкретного описания оповещения.
- Реализовать обработчик, на стороне 1С для события документа "ПриНажатии", который по идентификатору обработчика оповещения найдёт в клиентском Кеше и вызовет обработчик 1С, передав в него данные события JS.
Плюсы такого подхода очевидны - почти вся логика реализуется кодом 1С, решение легко поддаётся отладке и его намного проще расширять и дорабатывать при необходимости. Не затрагивая при этом основной блок взаимодействия 1C <-> JS.
Сам проект доступен в репозитории на github. Буду рад, если кто-то захочет помочь в развитии, так как сам не особо шарю в javascript, да и свободного времени не столь много.
Теперь пробежимся по возможностям подсистемы на текущий момент, и немного о планах на будущее.
В подсистему включён макет, добавляющий функции js. Сейчас реализовано:
- функция Eval, предоставляющая доступ к вызову функции eval и возвращающая объект с результатом выполнения или описанием ошибки.
- переопределение прототипа функций addEventListener и removeEventListener, для возможности получения списка активных подписок на события по каждому элементу DOM модели документа.
- добавлен объект, для управления текущими интервалами и таймаутами, с возможностью получения списка обработчиков. (setTimeout и setInterval).
- функции, для исследования объектов, переданных в метод (можно передавать объекты типа ВнешнийОбъект и получать структуру всех методов , свойств и итераторов объекта).
- функции, для вызова события элемента формы "ПриНажатии" с передачей особых управляющих событий. Сейчас реализованы события
- message1C(вызывает функцию 1С Сообщить в контексте текущей формы, позволяет показывать сообщения из кода js)
- error1C(вызывает исключение с текстом, переданным из js)
- log1C(производить запись в журнал регистрации из js)
- callback1C(вызывает определённый обработчик оповещения в 1С по сохранённому идентификатору). Реализует основную идею моей подсистемы.
- функция, позволяющая вызывать у объекта с типом ВнешнийОбъект любой метод по имени с передачей произвольного количества параметров. Параметры также могут представлять из себя Внешние Объекты (например ссылки на другие функции).
- Функция, позволяющая прочитать текущее свойство внешнего объекта по имени. По сути вызов функции идентичен получению значения свойства из ВнешнегоОбъекта через точку, но есть подозрение, что js выполняется в отдельных потоках и не привязан к основному потоку выполнения клиента 1С. А значит текущее значение в поле внешнего объекта может отличаться от реального...Но это не точно.. возможно 1С реализовали опрос текущего значения свойства внешнего объекта каждый раз при обращении, так как по сути внешний объект является ссылкой на конкретный объект JS и вполне реально при каждом обращении к его свойствам зачитывать текущее состояние из js.
Для кеширования обработчиков оповещения используется глобальная клиентская переменная. Хранения данных в ней реализовано в разрезе конкретной формы и поля с типом ДокументHTML. Тоесть в случае использования нескольких форм у нас сохранённые данные не перемешаются.
Чтобы использовать возможности расширения в своих разработках достаточно на форме при создании на сервере вызвать метод расширения ПодключитьРасширенныеСкрипты (чтобы изменить документ HTML и 1С его перезагрузила, тем самым выполнив наши подключаемые скрипты - надо делать это с сервера).
Ну и на сладенькое - Реализация обмена с 1С через WebSocket в пару строк:
Ну вот и всё, что на данный момент реализовано.
В планах на будущее:
- Реализация справочника "Подключаемые библиотеки", который позволит сохранять внешние библиотеки JS и CSS. Типа Amcharts, Bootstrap и тд..
- Доработка программного интерфейса, для подключения внешних библиотек в конкретным формам и полям HTML.
- Реализация обработки для настройки способов подключения файлов, указание порядка загрузки файлов. Настройка места подключения - в header или в теле документа.
- ну и на самую далёкую перспективу - реализация конструктора html, для создания интерфейса в самой 1С, визуальная настройка, привязка обработчиков и событий элементов DOM модели js к именам обработчиков 1С - думаю по имени модуля и имени процедуры обработчика. Как будут настраиваться дополнительные параметры при создании описания оповещения пока не придумал... (сейчас довольно призрачное представление, как всё это будет выглядеть).