На текущий момент штатные средства 1С в управляемых формах не могут в полной мере удовлетворить потребность в богатом и отзывчивом интерфейсе. Ведь, по словам разработчиков платформы, управляемые формы призваны обеспечить быструю и эффективную разработку бизнес-логики приложения, с некоторым ущербом интерфейсу. Для абсолютного большинства ситуаций это верный подход, но есть случаи, когда необходимо и то, и другое.
Решением является использование HTML и JavaScript.
Предлагается решение, которое работает как в тонком, так и в веб-клиенте. Под Windows и Linux.
Примечание: при упоминании работоспособности в тонком клиенте, также подразумевается работоспособность и в толстом клиенте (управляемое приложение).
Взаимодействие кода 1С и JavaScript.
Вызов методов языка JavaScript из кода 1С.
Методы JavaScript могут быть вызваны из 1C так:
<Окно документа>.funcName(funcArgs);
Глобальные переменные доступны следующим образом:
<Окно документа>.varName;
Для браузера Internet Explorer окно документа доступно через
Элементы.ПолеHTMLДокумента.Документ.parentWindow
Для остальных браузеров через
Элементы.ПолеHTMLДокумента.Документ.defaultView
Подготовка переменных модуля для взаимодействия с JavaScript и вызов функции:
&НаКлиенте
Перем ДокументПервогоБраузера;
&НаКлиенте
Перем ОкноПервогоБраузера;
&НаКлиенте
Процедура ПервыйБраузерДокументСформирован(Элемент)
// Сохранение элементов document и window в переменные модуля формы,
// для последующего быстрого доступа к элементам и функциям.
ДокументПервогоБраузера = Элемент.Документ;
ОкноПервогоБраузера = ДокументПервогоБраузера.parentWindow; // IE
Если ОкноПервогоБраузера = Неопределено Тогда
ОкноПервогоБраузера = ДокументПервогоБраузера.defaultView; // Прочие браузеры
КонецЕсли;
ОкноПервогоБраузера.createButtons(МассивКнопокПервогоБраузера);
КонецПроцедуры
Подробнее остановимся на передаваемых агрументах.
В тонком клиенте под Windows, помимо примитивных типов Строка, Число, Булево.., в функцию JavaScript можно передавать и нативные объекты 1С, такие как Структура, Массив, Соответствие, УправляемаяФорма, и др. Затем, в теле javascript функции доступны свойства и методы этих объектов.
Пример:
&НаКлиенте
Процедура СоздатьКнопки(Команда)
МассивКнопок = Новый Массив;
...
Для Сч = 1 По КоличествоКнопок Цикл
...
СтруктураКнопки = Новый Структура;
СтруктураКнопки.Вставить("Заголовок", ЗаголовокКнопки);
СтруктураКнопки.Вставить("ИмяКоманды", ИмяКоманды);
СтруктураКнопки.Вставить("РазмерТекста", РазмерТекста);
СтруктураКнопки.Вставить("ЦветТекста", ЦветТекста);
МассивКнопок.Добавить(СтруктураКнопки);
КонецЦикла;
ОкноПервогоБраузера.createButtons(МассивКнопок);
КонецПроцедуры
function createButtons(buttonsArray)
{
var buttonsCount = buttonsArray.Количество(); // Доступны методы и свойства массива 1С
for(var i = 0; i < buttonsCount; i++)
{
var ПараметрыКнопки = buttonsArray.Получить(i);
var btn = document.createElement('div');
btn.id = ПараметрыКнопки.ИмяКоманды;
btn.innerText = ПараметрыКнопки.Заголовок;
btn.style.fontSize = ПараметрыКнопки.РазмерТекста;
btn.style.color = ПараметрыКнопки.ЦветТекста;
container.appendChild(btn);
}
}
Данный метод очень удобен, и он подходит, если хватает возможности работы в тонком клиенте в ОС Windows.
В тонком клиенте под Linux, а также в веб-клиенте метод не работает. При передаче структур, массивов, соответствий в аргумент функции, в её теле эти агрументы равны undefined. Работает передача строк, чисел, булев.
Решением возникшей проблемы является использование формата обмена данными JSON. Что это такое - см. https://ru.wikipedia.org/wiki/JSON
Массивы, Структуры и Соответствия преобразуются в их строковое представление, и передаются в функцию. Затем, в теле функции они преобразовываются в объекты языка javascript, такие как Array и Object.
Функции сериализации и десериализации в/из JSON:
&НаКлиенте
Функция СериализоватьВJSON(СериализуемыйОбъект)
#Если ВебКлиент Тогда
СтрокаJSON = СериализоватьВJSONНаСервере(СериализуемыйОбъект);
#Иначе
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
НастройкиСериализации = Новый НастройкиСериализацииJSON();
НастройкиСериализации.СериализовыватьМассивыКакОбъекты = Ложь;
ЗаписатьJSON(ЗаписьJSON, СериализуемыйОбъект, НастройкиСериализации, "ПреобразованиеВJSON", ЭтотОбъект);
СтрокаJSON = ЗаписьJSON.Закрыть();
#КонецЕсли
Возврат СтрокаJSON;
КонецФункции
&НаСервереБезКонтекста
Функция СериализоватьВJSONНаСервере(СериализуемыйОбъект)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
НастройкиСериализации = Новый НастройкиСериализацииJSON();
НастройкиСериализации.СериализовыватьМассивыКакОбъекты = Ложь;
ЗаписатьJSON(ЗаписьJSON, СериализуемыйОбъект);
// ЗаписатьJSON(ЗаписьJSON, СериализуемыйОбъект, НастройкиСериализации, "ПреобразованиеВJSON", ЭтотОбъект);
//
// ЭтотОбъект недоступнен в безконтекстном вызове, поэтому нужно либо поместить эту функцию в общий модуль,
// либо изменить директиву компиляции в &НаСервере
СтрокаJSON = ЗаписьJSON.Закрыть();
Возврат СтрокаJSON;
КонецФункции
&НаКлиентеНаСервереБезКонтекста
Функция ПреобразованиеВJSON(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
// Данная функция вызывается для всех свойств, тип которых не поддерживает преобразование в формат JSON напрямую.
// Они нам не нужны, поэтому всегда отказ от их записи.
Отказ = Истина;
// Можно сделать преобразование в строку.
// Значение = Строка(Значение);
// Возврат Значение;
КонецФункции
&НаКлиенте
Функция ДесериализоватьИзJSON(СтрокаJSON)
#Если ВебКлиент Тогда
ДесериализованныйОбъект = ДесериализоватьИзJSONНаСервере(СтрокаJSON);
#Иначе
ЧтениеJSON = Новый ЧтениеJSON();
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
ДесериализованныйОбъект = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
#КонецЕсли
Возврат ДесериализованныйОбъект;
КонецФункции
&НаСервереБезКонтекста
Функция ДесериализоватьИзJSONНаСервере(СтрокаJSON)
ЧтениеJSON = Новый ЧтениеJSON();
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
ДесериализованныйОбъект = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Возврат ДесериализованныйОбъект;
КонецФункции
Используются механизмы работы с JSON, реализованные в платформе 8.3.6.1977.
Для платформы 8.3.5 и ранее, можно использовать разработку господина Александра Переверзева, доступную по адресу //infostart.ru/public/119601/.
Теперь, прежде чем передавать структуру в функцию JavaScript, преобразуем её в строку:
JSONМассивКнопокПервогоБраузера = СериализоватьВJSON(МассивКнопокПервогоБраузера);
ОкноПервогоБраузера.createButtons(JSONМассивКнопокПервогоБраузера);
Изменится и код функции Javascript, так как строку нужно преобразовать обратно в объект, пусть и не 1С-овский, но удобный для обработки:
function createButtonsWithJSON(buttonsData)
{
var buttonsData = JSON.parse(buttonsData);
for(var i in buttonsData)
{
var ПараметрыКнопки = buttonsData[i];
var btn = document.createElement('li');
btn.id = ПараметрыКнопки.ИмяКоманды;
...
}
}
Глобальный объект JSON, используемый для сериализации и парсинга (десериализации) в javascript, доступен начиная с Internet Explorer 8.
HTML документ в тонком клиенте, по умолчанию, даже при наличии установленного IE11, отображается в режиме совместимости с IE5 (Quirks mode), поэтому необходимо его перевести в Standarts mode установной тега <meta http-equiv="X-UA-Compatible" content="IE=EDGE" /> в теле тега head.
Если на компьютере установлен IE7 (когда переход в режим минимум IE8 невозможен), то нужно использовать следующий скрипт: https://github.com/douglascrockford/JSON-js/blob/master/json2.js
который реализует функционал работы с JSON в IE < 8 (по аналогии с работой с JSON в платформе начиная с версии 8.3.6.1977, и до неё).
В функции javascript можно создавать массивы или объекты javascript, заполнять их, и возвращать в 1C.
&НаКлиенте
Процедура ПолучитьОбъектИзJavaScript(Команда)
JSONСтруктураОтвета = ОкноПервогоБраузера.getDataFromJavaScript("");
// Нужно передать любой параметр, например пустую строку, иначе функция не выполняется,
// а переменной принимающей результат присваивается ссылка на эту функцию (COMОбъект).
СтруктураОтвета = ДесериализоватьИзJSON(JSONСтруктураОтвета);
Для Каждого КлючИЗначение Из СтруктураОтвета Цикл
Сообщить("Ключ: " + КлючИЗначение.Ключ + " Значение: " + КлючИЗначение.Значение);
КонецЦикла;
КонецПроцедуры
function getDataFromJavaScript()
{
var arr = []; // Массив
arr.push(555);
arr.push("Строка в массиве");
var obj = {}; // Объект (структура)
obj["Ключ1"] = "Пробная строка";
obj["Ключ2"] = 7777;
obj["Ключ3"] = true;
obj["Ключ4"] = arr;
return JSON.stringify(obj);
}
Вызов методов 1С из JavaScript
В тонком клиенте под Windows доступен очень удобный метод:
В HTML документе создается глобальная переменная, например externalForm. При загрузке документа, переменной externalForm присваивается ссылка на текущую упрваляемую форму, доступную через ЭтотОбъект или ЭтаФорма.
Затем, в коде JavaScript становятся доступны любые клиентские экспортные методы и переменные модуля данной управляемой формы.
Пример:
// В документе html, в теге script объявляем глобальную переменную.
var externalForm = null;
var timer = null;
var counter = 0;
function startTimer()
{
alert(externalForm.ПеременнаяМодуля);
timer = setInterval(function()
{
externalForm.МетодВызываемыйИзJavaScript("Значение переменной conunter: " + counter++);
}, 1000);
}
function stopTimer()
{
clearInterval(timer);
}
&НаКлиенте
Перем ПеременнаяМодуля Экспорт;
&НаКлиенте
Процедура ПриОткрытии(Отказ)
...
ПеременнаяМодуля = "Значение переменной модуля";
КонецПроцедуры
&НаКлиенте
Процедура ПервыйБраузерДокументСформирован(Элемент)
...
ОкноПервогоБраузера.externalForm = ЭтаФорма;
КонецПроцедуры
&НаКлиенте
Процедура ЗапуститьТаймер(Команда)
ОкноПервогоБраузера.startTimer();
КонецПроцедуры
&НаКлиенте
Процедура ОстановитьТаймер(Команда)
ОкноПервогоБраузера.stopTimer();
КонецПроцедуры
&НаКлиенте
Процедура МетодВызываемыйИзJavaScript(Данные) Экспорт
Сообщить(Данные);
КонецПроцедуры
В тонком клиенте на Linux и в веб-клиенте методы и свойства переменной externalForm не доступны в javascript, их значение - undefined, поэтому используется следующая методика:
Объявляем глобальную переменную javascript, в которую будем помещать передаваемое значение.
var interactionVariable = null;
В документ добавляем невидимую кнопку
<button id="interactionButton" style="display: none">Кнопка взаимодействия</button>
Присваиваем передаваемое значение, например имя необходимой функции или любое другое значение, переменной interactionVariable и вызываем нажатие невидимой кнопки.
interactionVariable = "МетодВызываемыйИзJavaScript через interactionButton. Значение переменной conunter: " + counter;
interactionButton.click();
И обрабатываем событие ПриНажатии у поля HTML документа.
&НаКлиенте
Процедура ПервыйБраузерПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
НажатыйЭлемент = ДанныеСобытия.Element;
Если НажатыйЭлемент.id = "interactionButton" Тогда
МетодВызываемыйИзJavaScript(ОкноПервогоБраузера.interactionVariable);
// Или: Заглушка = Вычислить(ОкноПервогоБраузера.interactionVariable + "()");
// Поле для фантазии большое
КонецЕсли;
КонецПроцедуры
Благодарю за внимание, не претендую на уникальность и надеюсь, что и данная информация будет полезна многим.
Прикреплена демонстрационная обработка.