Как вызвать JS-функцию с параметрами из макета 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними сервисами разработчики часто сталкиваются с необходимостью выполнения сложных алгоритмов шифрования, обсчета хэш-сумм или рендеринга данных, которые уже реализованы на языке JavaScript. Переписывать готовые JS-библиотеки на язык 1С ресурсоемко и не всегда возможно. Гораздо эффективнее хранить JS-код в макете конфигурации и вызывать его функции напрямую из платформы.

В этой статье мы подробно разберем, как правильно загрузить JavaScript из текстового макета 1С, передать в него параметры из управляемой формы и получить результат вычислений обратно в 1С. Мы также проанализируем распространенные ошибки разработчиков и особенности работы современных версий платформы 1С:Предприятие 8.3.

Почему старые методы интеграции больше не работают?

Ранее для выполнения произвольного JS-кода в среде Windows часто использовался COM-объект MSScriptControl.ScriptControl. Однако сегодня этот подход считается устаревшим и крайне ненадежным по следующим причинам:

  1. Платформозависимость: Компонент является исключительно 32-битным элементом Windows ActiveX. Он принципиально не работает на 64-битных клиентах 1С, серверах под управлением Linux или macOS.
  2. Безопасность: Использование внешних COM-объектов часто блокируется современными политиками безопасности операционных систем.
  3. Ограничения синтаксиса: MSScriptControl поддерживает только старые стандарты JavaScript (ES3/ES5) и не способен обрабатывать современный JS-код.

Единственным кроссплатформенным и современным решением в экосистеме 1С является использование элемента формы Поле HTML документа.

Начиная с версии платформы 1С 8.3.14, внутренний движок отображения HTML был изменен. Вместо устаревшего системного Internet Explorer (Trident) теперь используется современный и быстрый WebKit (Chromium). Это нововведение изменило правила обращения к элементам DOM-модели, в частности, свойство parentWindow уступило место стандартному defaultView.

Выясним причину, по которой у автора темы на форуме возникла ошибка "Метод объекта не обнаружен". Типичная ошибка начинающих разработчиков заключается в попытке мгновенного вызова JS-функции сразу после присвоения текста макета реквизиту формы. Загрузка HTML-документа и инициализация скриптов внутри браузерного движка происходят асинхронно. Попытка обратиться к функции до завершения рендеринга страницы всегда будет приводить к ошибке.


Пошаговое руководство по вызову JS-функции с параметрами

Рассмотрим по шагам правильный и стабильный алгоритм решения этой задачи.

Шаг 1. Подготовка текстового макета конфигурации

Создадим в нашей обработке или документе макет с типом Текстовый документ и назовем его МодульJS. Вставим в него полноценный HTML-код, содержащий JavaScript-функцию, к которой мы планируем обращаться. Обратите внимание на наличие тегов структуры документа:


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <script type="text/javascript">
        // Наша целевая функция, выполняющая условное шифрование или подпись данных
        function getDigest(objStr) {
            try {
                // Пример простой обработки строки для демонстрации результата
                var result = "Хэш для строки [" + objStr + "] успешно сформирован!";
                return result;
            } catch (e) {
                return "Ошибка выполнения JS: " + e.message;
            }
        }
    </script>
</head>
<body>
    <div>Контекст JS-скрипта инициализирован.</div>
</body>
</html>

Шаг 2. Настройка управляемой формы 1С

Добавим на управляемую форму новый реквизит и настроим элемент управления:

  1. Создаем реквизит формы с именем РеквизитHTML, тип данных — Строка (длина неограниченна).
  2. Перетаскиваем этот реквизит на рабочую область формы.
  3. Для созданного элемента формы в палитре свойств устанавливаем вид Поле HTML документа. Убедимся, что имя элемента формы установилось как ПолеHTML.
  4. В свойствах элемента ПолеHTML находим раздел "События" и создаем обработчик для события ПриНажатии или ДокументСформирован. Нам потребуется именно событие ДокументСформирован.

Добавим в модуль формы переменную-флаг, которая будет сигнализировать о готовности документа к работе:


&НаКлиенте
Перем ДокументЗагружен;

Шаг 3. Загрузка макета и ожидание готовности страницы

Реализуем чтение макета на сервере и его передачу в поле HTML-документа на клиенте при открытии формы. Также настроим обработчик завершения загрузки:


&НаСервере
Функция ПолучитьТекстМакетаНаСервере()
    // Получаем макет "МодульJS" из нашей обработки
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    Макет = ОбработкаОбъект.ПолучитьМакет("МодульJS");
    Возврат Макет.ПолучитьТекст();
КонецФункции

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ДокументЗагружен = Ложь;
    // Загружаем HTML-текст в реквизит формы, связанный с Полем HTML документа
    РеквизитHTML = ПолучитьТекстМакетаНаСервере();
КонецПроцедуры

&НаКлиенте
Процедура ПолеHTMLДокументСформирован(Элемент)
    // Данное событие срабатывает, когда веб-страница полностью отрисовалась и JS готов к работе
    ДокументЗагружен = Истина;
КонецПроцедуры

Шаг 4. Безопасное обращение к функции JS из 1С

Напишем функцию, которая будет извлекать глобальный контекст окна браузера с учетом особенностей различных версий платформы 1С (совместимость с IE и WebKit), а затем вызывать целевой JS-метод с передачей параметров:


&НаКлиенте
Функция ПолучитьКонтекстОкна(ЭлементHTML)
    Документ = ЭлементHTML.Документ;
    Если Документ = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    // В современном WebKit используется defaultView, в старом IE - parentWindow
    КонтекстОкна = Документ.defaultView;
    Если КонтекстОкна = Неопределено Тогда
        КонтекстОкна = Документ.parentWindow;
    КонецЕсли;
    
    Возврат КонтекстОкна;
КонецФункции

&НаКлиенте
Функция ВызватьШифрованиеJS(Знач ИсходныеДанные)
    
    Если Не ДокументЗагружен Тогда
        ПоказатьПредупреждение(, "Пожалуйста, подождите, HTML-документ еще загружается!");
        Возврат Неопределено;
    КонецЕсли;
    
    ЭлементHTML = Элементы.ПолеHTML;
    КонтекстОкна = ПолучитьКонтекстОкна(ЭлементHTML);
    
    Если КонтекстОкна = Неопределено Тогда
        ВызватьИсключение "Не удалось получить доступ к окну браузера.";
    КонецЕсли;
    
    Попытка
        // Обращаемся напрямую к JS-функции, передавая параметры
        РезультатJS = КонтекстОкна.getDigest(ИсходныеДанные);
        Возврат РезультатJS;
    Исключение
        Сообщить("Произошла ошибка при вызове getDigest: " + ОписаниеОшибки(), СтатусСообщения.Важное);
        Возврат Неопределено;
    КонецПопытки;
    
КонецФункции

Теперь мы можем привязать вызов этой функции к любой кнопке на нашей форме:


&НаКлиенте
Процедура ВыполнитьКоманду(Команда)
    Результат = ВызватьШифрованиеJS("Тестовая строка для передачи в JS");
    Если Результат <> Неопределено Тогда
        ПоказатьПредупреждение(, Результат);
    КонецЕсли;
КонецПроцедуры

Продвинутый метод: Динамическая инъекция скриптов

Рассмотрим альтернативную ситуацию. Допустим, ваш макет МодульJS содержит исключительно чистый JavaScript-код, без обертки в HTML-теги. Создавать под каждый такой скрипт статичную HTML-структуру неудобно. В этом случае мы можем применить технологию динамического внедрения (инъекции) JS-кода в пустой документ.

Для реализации этого метода загрузим в Поле HTML документа простейшую базовую структуру, а затем программно добавим наш скрипт в элемент head:


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ДокументЗагружен = Ложь;
    // Загружаем пустую базовую страницу-заглушку
    РеквизитHTML = "<html><head></head><body></body></html>";
КонецПроцедуры

&НаКлиенте
Процедура ПолеHTMLДокументСформирован(Элемент)
    ДокументЗагружен = Истина;
    
    // Получаем чистый JS-код из макета
    ТекстСкрипта = ПолучитьТекстМакетаНаСервере();
    
    // Внедряем скрипт в DOM-модель документа
    ДокументHTML = Элементы.ПолеHTML.Документ;
    НовыйЭлементСкрипт = ДокументHTML.createElement("script");
    НовыйЭлементСкрипт.type = "text/javascript";
    НовыйЭлементСкрипт.text = ТекстСкрипта;
    
    ДокументHTML.head.appendChild(НовыйЭлементСкрипт);
    // Теперь функции из макета добавлены в глобальный контекст defaultView
КонецПроцедуры

Этот способ позволяет использовать одно поле HTML для поочередной загрузки и выполнения совершенно разных JavaScript-библиотек "на лету" без перезагрузки всей страницы.


Передача сложных структур через JSON

Когда требуется передать в JS не простую строку или число, а сложный объект (например, массив структур, содержащих таблицы данных), стандартные COM-объекты преобразования типов могут отработать некорректно или обрезать типы данных.

Для надежного обмена сложными данными всегда используйте сериализацию в формат JSON. Проанализируем пример такого взаимодействия:

  1. В коде 1С упакуйте ваши структуры и массивы в JSON-строку с помощью стандартного объекта ЗаписьJSON.
  2. Передайте эту JSON-строку в качестве аргумента в JS-функцию.
  3. Внутри JavaScript первым делом декодируйте строку обратно в объект: var data = JSON.parse(objStr);.
  4. После выполнения вычислений упакуйте ответ в строку внутри JS: return JSON.stringify(resultObj);.
  5. В коде 1С десериализуйте полученный результат при помощи ЧтениеJSON.

Этот подход гарантирует 100% сохранность типов данных и защитит от непредвиденных ошибок преобразования при работе на любых версиях операционных систем.

← На главную