How-to: API контроллера пользовательского представления
Пользовательское представление, написанное на JavaScript, взаимодействует с формой через объект контроллера. В React-представлении контроллер доступен как props.controller; тот же контроллер передаётся и в JavaScript-функцию, привязанную действием INTERNAL CLIENT. С его помощью представление задаёт текущий объект группы, изменяет значения свойств, запрашивает подсказки и вызывает действия или скрипты на сервере.
Свойства и действия адресуются по их интеграционному имени — имени на форме (либо псевдониму / интеграционному имени NEW / DELETE кнопки), тому же имени, что использует внешний JSON/REST API.
Изменение текущего объекта и значений свойств
controller.changeObject(groupSID, object) задаёт текущий объект группы groupSID. Здесь object — это строка данных этой группы либо непосредственный дескриптор objects (см. правила идентификации строки ниже), но не голый row.key.
controller.changeProperty(property, value) изменяет property для текущего объекта группы. Чтобы адресовать конкретную строку, она передаётся посередине: controller.changeProperty(property, object, value), где object — строка данных либо непосредственный дескриптор. Когда property — это действие (или свойство без изменяемого значения), значение опускается: controller.changeProperty('edit') выполняет его на текущем объекте, controller.changeProperty('edit', object) — на переданной строке.
function orderView(props) {
const controller = props.controller;
return (
<div>
<button onClick={() => controller.changeProperty('note', 'checked')}>Отметить</button>
{props.data.o.list.map(row =>
<div key={row.key} onClick={() => controller.changeObject('o', row)}>
{row.number}
</div>)}
</div>
);
}
В форме changeProperty(property, X) с двумя аргументами платформа определяет, значение X или строка: если свойство принимает значение и X разрешается в строку (строка данных либо непосредственный дескриптор), то X читается как строка и вызов выполняется на ней; иначе X — это значение, и изменяется текущий объект.
controller.changeProperties(properties, objects, values) применяет несколько изменений сразу из параллельных массивов — properties[i] изменяется на values[i] для objects[i] (элемент может быть null для текущего объекта). Необязательный четвёртый массив groupSIDs привязывает каждое свойство к группе, когда его интеграционное имя не уникально в пределах формы.
controller.changeProperties(['note', 'qty'], [null, row], ['checked', 5]);
Запрос значений
controller.getPropertyValues запрашивает у сервера ограниченный список подсказок для свойства. Результат передаётся в обработчик ok в виде { data: [ { displayString, rawString, objects }, ... ], more }; more равно true, когда список усечён, поэтому это список подсказок, а не полный SELECT DISTINCT. В классическом пользовательском представлении GRID тот же запрос доступен как getValues(property, value, ok, fail) — это эквивалент getPropertyValues в режиме по умолчанию 'objects'; на уровне формы / в контроллере React используется getPropertyValues.
controller.getPropertyValues(property[, object], value[, mode], ok, fail[, count]);
-
value— вводимая строка запроса для сопоставления. -
object— необязательная строка (строка данных либо непосредственный дескриптор), ограничивающая запрос этой строкой; опускается для текущего объекта. -
mode— одно из:modeрезультат item.objects'objects'(по умолчанию)подходящие OBJECTSсвойства — выбор объектанепосредственный дескриптор objectsэтого объекта'values'различные значения свойства null(используйтеdisplayString/rawString)'change'подсказки свойства при редактировании зависит от свойства 'change'отражает то, как свойство редактируется — списокINPUT, ограничениеnotNullлибо собственное действие изменения, — а не уже присутствующие различные значения. Для свойства, которое в этом контексте изменить нельзя (например, свойство только для чтения или вычисляемое свойство без действия изменения), возвращается пустой список, тогда как'values'по-прежнему возвращает его различные значения. -
ok(result)/fail()— обработчики успеха и ошибки. -
count— увеличивает число запрашиваемых элементов, для постраничной загрузки.
item.objects из результата режима 'objects' можно передать напрямую обратно в changeObject или changeProperty, чтобы действовать с выбранным объектом:
controller.getPropertyValues('customer', text, 'objects',
result => result.data.forEach(item => console.log(item.displayString)),
() => console.log('ошибка'));
// выбор первой подсказки как заказчика группы
controller.getPropertyValues('customer', text, 'objects', result => {
const item = result.data[0];
if (item) controller.changeObject('c', item.objects);
}, () => {});
Вызов сервера
exec, eval, evalAction и change выполняются на сервере и возвращают Promise. Они работают в точности так, как описано в How-to: Пользовательские компоненты (объекты) — те же проверки доступа и то же преобразование результата в JS-значение.
controller.exec(action, ...params)— выполняет именованное действие; разрешается его значениемRETURN.controller.eval(script, ...params)— выполняет lsf-скрипт, определяющий собственное действиеrun(типизированные параметры).controller.evalAction(script, ...params)— выполняет тело действия, обёрнутое в действиеrun, с параметрами$1,$2, ….controller.change(property, ...keyParams, value)— изменяет глобальное свойство; последний аргумент — значение, предшествующие — ключи.
const total = await controller.exec('recalc', orderId);
const doubled = await controller.eval('run(INTEGER a) { RETURN a * 2; }', 21); // 42
await controller.change('note', orderId, 'checked');
Вызов, сделанный после закрытия формы, отклоняется с ошибкой Form is closed — он никогда не зависает, поэтому await на закрытой форме попадает в ветку catch.
Правила идентификации строки
Метод, адресующий строку, принимает одно из:
- объект строки данных, полученный представлением (из props React / списка
update); - spread- или
Object.assign-клон такой строки — перечислимый дескрипторobjectsкопируется вместе с ней, поэтому клон разрешается в тот же объект; - непосредственный дескриптор
objects—row.objectsлибоitem.objectsиз результатаgetPropertyValuesв режиме'objects'.
Голый row.key не принимается: key — это токен для отображения / ключа React / сравнения при сопоставлении, а не вход для разрешения. Имена полей key, isCurrent и objects зарезервированы в строке — свойство или колонка приложения с одним из этих интеграционных имён было бы перезаписано.
Если явный аргумент-объект не разрешается ни в строку, ни в непосредственный дескриптор, платформа не подставляет молча текущую строку и не выбрасывает исключение: changeProperty записывает ошибку в консоль и пропускает это изменение, а changeObject ничего не делает.
Совместимость с классическим CUSTOM
Классические пользовательские компоненты на render/update сохраняют собственный controller с помощниками render/update/isCurrent/getDiff и методом события controller.change. Их строки теперь тоже несут публичный key и перечислимый дескриптор objects, поэтому строку такого представления можно передавать всюду, где ожидается строка. Вызов controller.changeProperty(property, ...) для свойства, которое не является колонкой собственной таблицы этого представления, делегируется контроллеру формы, который разрешает свойство в пределах всей формы, — так классическое представление может изменить свойство, которое не отображает.